️ 完善组件样式 #64

*优化换行符渲染
*增加网页活动识别
*调整折叠文本样式
*限制最大宽度而非写死宽度
This commit is contained in:
BTMuli
2023-12-06 20:21:00 +08:00
parent 0f07f8c031
commit 03b83ba2d1
5 changed files with 181 additions and 55 deletions

View File

@@ -34,6 +34,7 @@ pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
mhy_window_config.url = get_mhy_client_url(func.clone()); mhy_window_config.url = get_mhy_client_url(func.clone());
} }
if func == "birthday" if func == "birthday"
|| func == "web_act"
|| url.starts_with("https://webstatic.mihoyo.com/ys/event/e20220303-birthday/index.html") || url.starts_with("https://webstatic.mihoyo.com/ys/event/e20220303-birthday/index.html")
{ {
mhy_window_config.width = 1280.0; mhy_window_config.width = 1280.0;

View File

@@ -1,19 +1,25 @@
<template> <template>
<div v-if="props.data.insert.lottery" @click="toLottery()" class="mys-post-link"> <div
v-if="props.data.insert.lottery"
@click="toLottery()"
class="tp-backup-lottery"
:title="`ID: ${props.data.insert.lottery.id}`"
>
<v-icon size="small">mdi-gift</v-icon> <v-icon size="small">mdi-gift</v-icon>
<span>{{ props.data.insert.lottery.toast }}</span> <span>{{ props.data.insert.lottery.toast }}</span>
</div> </div>
<div v-else-if="props.data.insert.fold" class="mys-post-div"> <details v-else-if="props.data.insert.fold" class="tp-backup-fold">
<details class="mys-post-details"> <summary class="tp-backup-summary">
<summary> <TpParser :data="JSON.parse(props.data.insert.fold.title)" />
<TpParser :data="JSON.parse(props.data.insert.fold.title)" /> </summary>
</summary> <div class="tp-backup-details">
<TpParser :data="JSON.parse(props.data.insert.fold.content)" /> <TpParser :data="JSON.parse(props.data.insert.fold.content)" />
</details> </div>
</div> </details>
<TpUnknown v-else :data="<TGApp.Plugins.Mys.SctPost.Empty>props.data" /> <TpUnknown v-else :data="<TGApp.Plugins.Mys.SctPost.Empty>props.data" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { toRaw } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import TpParser from "./tp-parser.vue"; import TpParser from "./tp-parser.vue";
@@ -40,6 +46,8 @@ interface TpBackupTextProps {
const props = defineProps<TpBackupTextProps>(); const props = defineProps<TpBackupTextProps>();
const router = useRouter(); const router = useRouter();
console.log("tpBackupText", props.data.insert.backup_text, toRaw(props.data));
async function toLottery() { async function toLottery() {
if (!props.data.insert.lottery) return; if (!props.data.insert.lottery) return;
await router.push({ await router.push({
@@ -50,3 +58,34 @@ async function toLottery() {
}); });
} }
</script> </script>
<style lang="css" scoped>
.tp-backup-lottery {
display: inline-flex;
align-items: center;
justify-content: center;
color: #00c3ff;
column-gap: 5px;
cursor: pointer;
}
.tp-backup-fold {
padding: 10px;
border: 1px solid var(--common-shadow-2);
border-radius: 10px;
margin: 10px auto;
}
.tp-backup-fold ::marker {
color: var(--common-shadow-4);
content: "";
}
.tp-backup-summary {
margin-left: 5px;
font-family: var(--font-title);
}
.tp-backup-details {
padding-left: 20px;
}
</style>

View File

@@ -1,8 +1,12 @@
<template> <template>
<div class="mys-post-divider" v-if="isInclude"> <div class="tp-divider-box" v-if="isInclude">
<img alt="divider" :src="`/source/post/divider_${props.data.insert.divider}.webp`" /> <img
alt="divider"
:src="`/source/post/divider_${props.data.insert.divider}.webp`"
:title="props.data.insert.divider"
/>
</div> </div>
<TpUnknown v-else :data="props.data" /> <TpUnknown v-else :data="emptyData" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed } from "vue";
@@ -20,8 +24,21 @@ interface TpDividerProps {
} }
const props = defineProps<TpDividerProps>(); const props = defineProps<TpDividerProps>();
const emptyData: TGApp.Plugins.Mys.SctPost.Empty = <TGApp.Plugins.Mys.SctPost.Empty>props.data;
console.log("tpDivider", props.data.insert.divider);
const isInclude = computed(() => { const isInclude = computed(() => {
const list = ["line_1", "line_2", "line_3", "line_4"]; const list = ["line_1", "line_2", "line_3", "line_4"];
return list.includes(props.data.insert.divider); return list.includes(props.data.insert.divider);
}); });
</script> </script>
<style lang="css" scoped>
.tp-divider-box {
margin: 10px auto;
}
.tp-divider-box img {
max-width: 100%;
}
</style>

View File

@@ -1,14 +1,18 @@
<template> <template>
<div class="mys-post-div"> <div class="tp-image-box">
<img <img
class="mys-post-img" :style="getImageStyle()"
:src="props.data.insert.image" :src="props.data.insert.image"
:alt="props.data.insert.image" :alt="props.data.insert.image"
:title="props.data.insert.image" :title="getImageTitle()"
/> />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { StyleValue, toRaw } from "vue";
import { bytesToSize } from "../../utils/toolFunc";
interface TpImage { interface TpImage {
insert: { insert: {
image: string; image: string;
@@ -16,8 +20,9 @@ interface TpImage {
attributes?: { attributes?: {
width: number; width: number;
height: number; height: number;
size: number | undefined; size?: number;
ext: string | undefined; ext?: "png" | "jpg"; // 待补充
align?: "center"; // 待补充
}; };
} }
@@ -26,4 +31,42 @@ interface TpImageProps {
} }
const props = defineProps<TpImageProps>(); const props = defineProps<TpImageProps>();
console.log("tp-image", props.data.insert.image, toRaw(props.data).attributes);
function getImageStyle(): StyleValue {
let style: StyleValue = <Array<StyleValue>>[];
if (props.data.attributes == undefined) return style;
if (props.data.attributes.width < 800) {
const widthFullRule: StyleValue = "width: 100%";
style.push(widthFullRule);
}
return style;
}
function getImageTitle(): string {
if (props.data.attributes == undefined) return "";
const res: string[] = [];
res.push(`宽度:${props.data.attributes.width}px`);
res.push(`高度:${props.data.attributes.height}px`);
if (props.data.attributes.size) {
const size = bytesToSize(props.data.attributes.size);
res.push(`大小:${size}`);
}
if (props.data.attributes.ext) {
res.push(`格式:${props.data.attributes.ext}`);
}
return res.join("\n");
}
</script> </script>
<style lang="css" scoped>
.tp-image-box {
margin: 10px auto;
}
.tp-image-box img {
max-width: 100%;
height: auto;
border-radius: 10px;
}
</style>

View File

@@ -1,29 +1,20 @@
<template> <template>
<span v-if="mode == 'link'" class="mys-post-link" @click="toLink()"> <span v-if="mode == 'link'" class="tp-text-link" @click="toLink()">
<v-icon size="small">mdi-link-variant</v-icon>{{ props.data.insert }} <v-icon size="small">mdi-link-variant</v-icon>{{ props.data.insert }}
</span> </span>
<img <span v-else-if="mode == 'emoji'" class="tp-text-emoji">
v-else-if="mode == 'emoji'" <img :src="getEmojiUrl()" :alt="getEmojiName()" :title="getEmojiName()" />
class="mys-post-emoji" </span>
:src="getEmojiUrl()" <span v-else :style="getTextStyle()" class="tp-text-span">
:alt="getEmojiName()" {{ props.data.insert }}
:title="getEmojiName()"
/>
<span v-else :style="getTextStyle()">
<span
v-if="props.data.insert.includes('\n')"
v-html="props.data.insert.replace(/\n/g, '<br />')"
/>
<span v-else>{{ props.data.insert }}</span>
</span> </span>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from "vue"; 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 { isColorSimilar, isMysPost } from "../../plugins/Mys/utils/parsePost";
import { useAppStore } from "../../store/modules/app";
import TGClient from "../../utils/TGClient"; import TGClient from "../../utils/TGClient";
import showConfirm from "../func/confirm"; import showConfirm from "../func/confirm";
import showSnackbar from "../func/snackbar"; import showSnackbar from "../func/snackbar";
@@ -46,6 +37,8 @@ const props = defineProps<TpTextProps>();
const mode = ref<string>("text"); const mode = ref<string>("text");
const router = useRouter(); const router = useRouter();
console.log("tpText", JSON.stringify(props.data.insert), toRaw(props.data)?.attributes);
onMounted(() => { onMounted(() => {
if (props.data.attributes && "link" in props.data.attributes) { if (props.data.attributes && "link" in props.data.attributes) {
mode.value = "link"; mode.value = "link";
@@ -57,30 +50,30 @@ onMounted(() => {
}); });
// 解析文本样式 // 解析文本样式
function getTextStyle(): string { function getTextStyle(): StyleValue {
const style: CSSStyleDeclaration = <CSSStyleDeclaration>{}; const style = <Array<StyleValue>>[];
const data: TpText = <TpText>props.data; const data: TpText = <TpText>props.data;
if (data.attributes) { if (data.attributes) {
if (data.attributes.bold) { if (data.attributes.bold) {
style.fontWeight = "bold"; const ruleBold: StyleValue = "fontFamily: var(--font-title)";
style.push(ruleBold);
} }
if (data.attributes.color) { if (data.attributes.color) {
let colorGet = data.attributes.color; let colorGet = data.attributes.color;
if (isColorSimilar("#000000", data.attributes.color)) { if (isColorSimilar("#000000", data.attributes.color)) {
colorGet = "var(--app-page-content);"; colorGet = "var(--app-page-content)";
} }
style.color = colorGet; const ruleColor: StyleValue = `color: ${colorGet}`;
style.push(ruleColor);
} }
// todo 这边效果不是很好
// refer: 45245869
if (data.attributes.align) { if (data.attributes.align) {
style.textAlign = data.attributes.align; const ruleAlign: StyleValue = `textAlign: ${data.attributes.align}`;
} style.push(ruleAlign);
if (props.data.insert.includes("\n")) {
style.whiteSpace = "pre-wrap";
} }
} }
return Object.keys(style) return style;
.map((key) => `${key}: ${style[key]}`)
.join(";");
} }
// 解析链接目标 // 解析链接目标
@@ -95,19 +88,26 @@ async function toLink() {
post_id: link.split("/").pop(), post_id: link.split("/").pop(),
}, },
}); });
} else if (link.startsWith("https://webstatic.mihoyo.com/ys/event/e20220303-birthday/")) { } else if (
if (useAppStore().isLogin) { link.startsWith("https://webstatic.mihoyo.com/ys/event/e20220303-birthday/") ||
await TGClient.open("birthday"); link.startsWith("https://act.mihoyo.com")
} else { ) {
const res = await showConfirm({ const resOpen = await showConfirm({
title: "跳转确认", title: "采用内置 JSBridge",
text: "未登录,是否采用内置 JSBridge 打开?(取消则使用浏览器打开", text: "取消则使用外部浏览器打开",
});
if (resOpen) {
const resType = await showConfirm({
title: "采用宽屏模式?",
text: "取消则使用默认竖屏",
}); });
if (res) { if (resType) {
await TGClient.open("birthday"); await TGClient.open("web_act", link);
} else { } else {
window.open(link); await TGClient.open("web_act_thin", link);
} }
} else {
window.open(link);
} }
} else { } else {
window.open(props.data.attributes.link); window.open(props.data.attributes.link);
@@ -134,10 +134,36 @@ function getEmojiUrl(): string {
}); });
} }
const emojiName = getEmojiName(); const emojiName = getEmojiName();
return JSON.parse(emojis)[emojiName]; return JSON.parse(<string>emojis)[emojiName];
} }
function getEmojiName() { function getEmojiName() {
return props.data.insert.slice(2, -1); return props.data.insert.slice(2, -1);
} }
</script> </script>
<style lang="css" scoped>
.tp-text-link {
display: inline-flex;
align-items: center;
justify-content: center;
color: #00c3ff;
cursor: pointer;
}
.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;
}
.tp-text-span {
white-space: pre-wrap;
}
</style>