♻️ 基础组件封装

This commit is contained in:
目棃
2025-01-13 14:25:34 +08:00
parent a7ca51dcc1
commit 9d995994ca
13 changed files with 96 additions and 96 deletions

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="tgn-container"> <div class="tgn-container">
<div v-for="navItem in nav" :key="navItem.id" class="tgn-nav" @click="toNav(navItem)"> <div v-for="navItem in nav" :key="navItem.id" class="tgn-nav" @click="toNav(navItem)">
<img alt="navIcon" :src="navItem.icon" /> <TMiImg alt="navIcon" :src="navItem.icon" :ori="true" />
<span>{{ navItem.name }}</span> <span>{{ navItem.name }}</span>
</div> </div>
<div v-if="props.modelValue === 2 && hasNav" class="tgn-nav"> <div v-if="props.modelValue === 2 && hasNav" class="tgn-nav">
@@ -11,6 +11,7 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showDialog from "@comp/func/dialog.js"; import showDialog from "@comp/func/dialog.js";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import Mys from "@Mys/index.js"; import Mys from "@Mys/index.js";

View File

@@ -0,0 +1,42 @@
<template>
<img
:src="localUrl"
:alt="props.alt"
:title="props.title"
v-if="localUrl"
@click="emits('click')"
:class="props.class"
/>
<v-progress-circular v-else indeterminate color="primary" size="25" />
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from "vue";
import { useAppStore } from "@/store/modules/app.js";
import { saveImgLocal } from "@/utils/TGShare.js";
type TMiImgProps = {
src: string;
alt: string;
title?: string;
class?: string;
ori?: boolean;
};
type TMiImgEmits = {
(e: "click"): void;
};
const props = defineProps<TMiImgProps>();
const emits = defineEmits<TMiImgEmits>();
const appStore = useAppStore();
const localUrl = ref<string>();
onMounted(async () => {
const link = props.ori ? props.src : appStore.getImageUrl(props.src);
localUrl.value = await saveImgLocal(link);
});
onUnmounted(() => {
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
});
</script>

View File

@@ -1,13 +1,7 @@
<template> <template>
<div :id="`anno_card_${props.modelValue.id}`" class="anno-card"> <div :id="`anno_card_${props.modelValue.id}`" class="anno-card">
<div class="anno-cover" :title="props.modelValue.title" @click="createAnno"> <div class="anno-cover" :title="props.modelValue.title" @click="createAnno">
<img :src="localBanner" alt="cover" v-if="localBanner" /> <TMiImg :src="props.modelValue.banner" alt="cover" :ori="true" />
<v-progress-circular
color="primary"
:indeterminate="true"
v-else-if="props.modelValue.banner !== ''"
/>
<img src="/source/UI/defaultCover.webp" alt="cover" v-else />
<div class="anno-info"> <div class="anno-info">
<div class="anno-time"> <div class="anno-time">
<v-icon>mdi-clock-time-four-outline</v-icon> <v-icon>mdi-clock-time-four-outline</v-icon>
@@ -19,55 +13,22 @@
{{ parseTitle(props.modelValue.subtitle) }} {{ parseTitle(props.modelValue.subtitle) }}
</div> </div>
<div class="anno-label" :title="`标签:${props.modelValue.tagLabel}`"> <div class="anno-label" :title="`标签:${props.modelValue.tagLabel}`">
<img :src="localTag" alt="tag" v-if="localTag" /> <TMiImg :src="props.modelValue.tagIcon" alt="tag" :ori="true" />
<v-icon v-else>mdi-tag</v-icon>
<span>{{ props.modelValue.tagLabel }}</span> <span>{{ props.modelValue.tagLabel }}</span>
</div> </div>
<div class="anno-id">{{ props.modelValue.id }}</div> <div class="anno-id">{{ props.modelValue.id }}</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import { onMounted, onUnmounted, ref, watch } from "vue";
import TGLogger from "@/utils/TGLogger.js"; import TGLogger from "@/utils/TGLogger.js";
import { generateShareImg, saveImgLocal } from "@/utils/TGShare.js"; import { generateShareImg } from "@/utils/TGShare.js";
import { createTGWindow } from "@/utils/TGWindow.js"; import { createTGWindow } from "@/utils/TGWindow.js";
type TAnnoCardProps = { region: string; modelValue: TGApp.App.Announcement.ListCard; lang: string }; type TAnnoCardProps = { region: string; modelValue: TGApp.App.Announcement.ListCard; lang: string };
const props = defineProps<TAnnoCardProps>(); const props = defineProps<TAnnoCardProps>();
const localBanner = ref<string>();
const localTag = ref<string>();
onMounted(async () => {
if (props.modelValue.banner !== "") {
localBanner.value = await saveImgLocal(props.modelValue.banner);
}
localTag.value = await saveImgLocal(props.modelValue.tagIcon);
});
watch(
() => props.modelValue,
async () => {
if (localBanner.value && localBanner.value.startsWith("blob:")) {
URL.revokeObjectURL(localBanner.value);
localBanner.value = undefined;
}
if (props.modelValue.banner !== "") {
localBanner.value = await saveImgLocal(props.modelValue.banner);
}
if (localTag.value && localTag.value.startsWith("blob:")) {
URL.revokeObjectURL(localTag.value);
localTag.value = undefined;
}
localTag.value = await saveImgLocal(props.modelValue.tagIcon);
},
);
onUnmounted(() => {
if (localBanner.value) URL.revokeObjectURL(localBanner.value);
if (localTag.value) URL.revokeObjectURL(localTag.value);
});
function parseTitle(title: string): string { function parseTitle(title: string): string {
const dom = new DOMParser().parseFromString(title, "text/html"); const dom = new DOMParser().parseFromString(title, "text/html");

View File

@@ -2,11 +2,11 @@
<TOverlay v-model="visible" blur-val="5px"> <TOverlay v-model="visible" blur-val="5px">
<div class="toab-container" v-if="props.data"> <div class="toab-container" v-if="props.data">
<div class="toab-img"> <div class="toab-img">
<img :src="props.data.take_picture[Number(props.choice)]" alt="顶部图像" /> <TMiImg :ori="true" :src="props.data.take_picture[Number(props.choice)]" alt="顶部图像" />
<div class="toab-dialog" v-show="showText"> <div class="toab-dialog" v-show="showText">
<div v-for="(item, index) in textParse" :key="index" class="toab-dialog-item"> <div v-for="(item, index) in textParse" :key="index" class="toab-dialog-item">
<div class="toab-dialog-item-icon" v-if="item.icon" :title="item.name"> <div class="toab-dialog-item-icon" v-if="item.icon" :title="item.name">
<img :src="item.icon" alt="对白头像" /> <TMiImg :src="item.icon" alt="对白头像" :ori="true" />
</div> </div>
<div v-else-if="item.name !== '未知'" class="toab-dialog-item-name"> <div v-else-if="item.name !== '未知'" class="toab-dialog-item-name">
{{ item.name }} {{ item.name }}
@@ -33,6 +33,7 @@
</TOverlay> </TOverlay>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import TMiImg from "@comp/app/t-mi-img.vue";
import TOverlay from "@comp/app/t-overlay.vue"; import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import { fetch } from "@tauri-apps/plugin-http"; import { fetch } from "@tauri-apps/plugin-http";

View File

@@ -6,20 +6,21 @@
</div> </div>
<div class="tcb-top-active" v-else> <div class="tcb-top-active" v-else>
<span>今天是</span> <span>今天是</span>
<img <TMiImg
v-for="i in cur" v-for="i in cur"
:key="i.role_id" :key="i.role_id"
class="tcb-cur" class="tcb-cur"
:alt="i.name" :alt="i.name"
:src="i.head_icon" :src="i.head_icon"
:title="i.name" :title="i.name"
:ori="true"
/> />
<span>的生日哦~</span> <span>的生日哦~</span>
<img @click="toBirth(true)" src="/source/UI/act_birthday.png" alt="empty" class="active" /> <img @click="toBirth(true)" src="/source/UI/act_birthday.png" alt="empty" class="active" />
</div> </div>
<div>即将到来{{ next[0].role_birthday }}</div> <div>即将到来{{ next[0].role_birthday }}</div>
<div v-for="i in next" :key="i.role_id" class="tcb-item"> <div v-for="i in next" :key="i.role_id" class="tcb-item">
<img :src="i.head_icon" :alt="i.name" @click="toBirth(i)" :title="i.name" /> <TMiImg :src="i.head_icon" :alt="i.name" @click="toBirth(i)" :title="i.name" :ori="true" />
<div class="tcb-item-info"> <div class="tcb-item-info">
<span>{{ i.name }} 所属{{ i.belong === "" ? "未知" : i.belong }}</span> <span>{{ i.name }} 所属{{ i.belong === "" ? "未知" : i.belong }}</span>
<span>{{ parseDesc(i.introduce) }}</span> <span>{{ parseDesc(i.introduce) }}</span>
@@ -28,6 +29,7 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import TSAvatarBirth from "@Sqlite/modules/avatarBirth.js"; import TSAvatarBirth from "@Sqlite/modules/avatarBirth.js";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { onBeforeMount, ref, shallowRef } from "vue"; import { onBeforeMount, ref, shallowRef } from "vue";

View File

@@ -9,7 +9,7 @@
<div class="pool-grid"> <div class="pool-grid">
<div v-for="pool in poolSelect" :key="pool.postId" class="pool-card"> <div v-for="pool in poolSelect" :key="pool.postId" class="pool-card">
<div class="pool-cover" @click="createPost(pool.postId, pool.title)"> <div class="pool-cover" @click="createPost(pool.postId, pool.title)">
<img :src="pool.cover" alt="cover" /> <TMiImg :src="pool.cover" alt="cover" :ori="true" />
</div> </div>
<div class="pool-bottom"> <div class="pool-bottom">
<div class="pool-character"> <div class="pool-character">
@@ -25,7 +25,7 @@
v-if="character.info" v-if="character.info"
:model-value="getCBox(character.info)" :model-value="getCBox(character.info)"
/> />
<img v-else :src="character.icon" alt="character" /> <TMiImg v-else :src="character.icon" alt="character" />
</div> </div>
</div> </div>
</div> </div>
@@ -56,6 +56,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TItembox, { type TItemBoxData } from "@comp/app/t-itemBox.vue"; import TItembox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import Mys from "@Mys/index.js"; import Mys from "@Mys/index.js";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";

View File

@@ -3,7 +3,7 @@
<div class="top"> <div class="top">
<div class="main"> <div class="main">
<div class="left" @click="openPosition(props.position)"> <div class="left" @click="openPosition(props.position)">
<img :src="props.position.icon" alt="icon" /> <TMiImg :src="props.position.icon" alt="icon" />
</div> </div>
<div class="right"> <div class="right">
<div class="title">{{ props.position.title }}</div> <div class="title">{{ props.position.title }}</div>
@@ -38,6 +38,7 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import type { PositionItem } from "@comp/pageHome/ph-comp-position.vue"; import type { PositionItem } from "@comp/pageHome/ph-comp-position.vue";
@@ -102,6 +103,7 @@ async function openPosition(card: TGApp.Plugins.Mys.Position.RenderCard): Promis
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 3px; border-radius: 3px;
background: var(--box-bg-2);
object-fit: contain; object-fit: contain;
img { img {

View File

@@ -5,7 +5,7 @@
<div :title="getAuthorDesc()">{{ getAuthorDesc() }}</div> <div :title="getAuthorDesc()">{{ getAuthorDesc() }}</div>
</div> </div>
<div class="tpa-img"> <div class="tpa-img">
<img :src="props.data.avatar_url" alt="avatar" class="tpa-icon" /> <img :src="avatarUrl" alt="avatar" class="tpa-icon" v-if="avatarUrl" />
<img <img
:src="props.data.pendant" :src="props.data.pendant"
alt="pendant" alt="pendant"
@@ -28,11 +28,26 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"; import { computed, onMounted, onUnmounted, ref } from "vue";
import { useAppStore } from "@/store/modules/app.js";
import { saveImgLocal } from "@/utils/TGShare.js";
type TpAvatarProps = { data: TGApp.Plugins.Mys.User.Post; position: "left" | "right" }; type TpAvatarProps = { data: TGApp.Plugins.Mys.User.Post; position: "left" | "right" };
const props = defineProps<TpAvatarProps>(); const props = defineProps<TpAvatarProps>();
const appStore = useAppStore();
const avatarUrl = ref<string>();
const pendantUrl = ref<string>();
onMounted(async () => {
avatarUrl.value = await saveImgLocal(appStore.getImageUrl(props.data.avatar_url));
});
onUnmounted(() => {
if (avatarUrl.value) URL.revokeObjectURL(avatarUrl.value);
if (pendantUrl.value) URL.revokeObjectURL(pendantUrl.value);
});
function getAuthorDesc(): string { function getAuthorDesc(): string {
if (props.data.certification.label !== "") return props.data.certification.label; if (props.data.certification.label !== "") return props.data.certification.label;

View File

@@ -1,6 +1,10 @@
<template> <template>
<div class="tp-emo-box" title="自定义表情" v-if="localUrl !== undefined"> <div class="tp-emo-box" title="自定义表情">
<img :src="localUrl" :alt="props.data.insert.custom_emoticon.hash" /> <TMiImg
:src="props.data.insert.custom_emoticon.url"
:alt="props.data.insert.custom_emoticon.hash"
:ori="true"
/>
<div <div
class="tp-emo-info" class="tp-emo-info"
v-if="props.data.insert.custom_emoticon.size.width > 100" v-if="props.data.insert.custom_emoticon.size.width > 100"
@@ -10,16 +14,13 @@
自定义表情 自定义表情
</div> </div>
</div> </div>
<div v-else class="tp-emo-load" :title="getImageUrl()">
<v-progress-circular :indeterminate="true" color="primary" size="small" />
<span>加载中...</span>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import { onMounted, onUnmounted, ref, shallowRef } from "vue"; import { shallowRef } from "vue";
import { getImageBuffer, saveCanvasImg, saveImgLocal } from "@/utils/TGShare.js"; import { getImageBuffer, saveCanvasImg } from "@/utils/TGShare.js";
import { bytesToSize } from "@/utils/toolFunc.js"; import { bytesToSize } from "@/utils/toolFunc.js";
type TpCustomEmoticon = { type TpCustomEmoticon = {
@@ -37,27 +38,10 @@ type TpCustomEmoticon = {
type TpEmoticonProps = { data: TpCustomEmoticon }; type TpEmoticonProps = { data: TpCustomEmoticon };
const props = defineProps<TpEmoticonProps>(); const props = defineProps<TpEmoticonProps>();
const localUrl = ref<string>();
const buffer = shallowRef<Uint8Array | null>(null); const buffer = shallowRef<Uint8Array | null>(null);
console.log("tp-emoticon", props.data.insert.custom_emoticon); console.log("tp-emoticon", props.data.insert.custom_emoticon);
onMounted(async () => {
const link = getImageUrl();
localUrl.value = await saveImgLocal(link);
});
onUnmounted(() => {
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
});
function getImageUrl(): string {
const img = props.data.insert.custom_emoticon.url;
const append = "?x-oss-process=image/format,png";
if (img.endsWith(".gif")) return img;
return img + append;
}
async function download(): Promise<void> { async function download(): Promise<void> {
const image = props.data.insert.custom_emoticon.url; const image = props.data.insert.custom_emoticon.url;
if (buffer.value === null) buffer.value = await getImageBuffer(image); if (buffer.value === null) buffer.value = await getImageBuffer(image);

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="tp-link-card-box"> <div class="tp-link-card-box">
<img :src="cover" alt="cover" @click="toLink()" v-if="cover" /> <TMiImg :src="props.data.insert.link_card.cover" alt="cover" @click="toLink()" />
<div class="tp-link-card-content"> <div class="tp-link-card-content">
<span>{{ props.data.insert.link_card.title }}</span> <span>{{ props.data.insert.link_card.title }}</span>
<div v-if="props.data.insert.link_card.price" class="tp-link-card-price"> <div v-if="props.data.insert.link_card.price" class="tp-link-card-price">
@@ -11,13 +11,12 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";
import { computed, onMounted, onUnmounted, ref, toRaw } from "vue"; import { computed, toRaw } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useAppStore } from "@/store/modules/app.js";
import { parseLink, parsePost } from "@/utils/linkParser.js"; import { parseLink, parsePost } from "@/utils/linkParser.js";
import { saveImgLocal } from "@/utils/TGShare.js";
type TpLinkCard = { type TpLinkCard = {
insert: { insert: {
@@ -40,9 +39,7 @@ type TpLinkCardProps = { data: TpLinkCard };
const props = defineProps<TpLinkCardProps>(); const props = defineProps<TpLinkCardProps>();
const router = useRouter(); const router = useRouter();
const appStore = useAppStore();
const cover = ref<string>();
const btnText = computed<string>(() => { const btnText = computed<string>(() => {
if (!props.data.insert.link_card.button_text || props.data.insert.link_card.button_text === "") { if (!props.data.insert.link_card.button_text || props.data.insert.link_card.button_text === "") {
return "详情"; return "详情";
@@ -50,15 +47,6 @@ const btnText = computed<string>(() => {
return props.data.insert.link_card.button_text; return props.data.insert.link_card.button_text;
}); });
onMounted(async () => {
const coverLink = appStore.getImageUrl(props.data.insert.link_card.cover);
cover.value = await saveImgLocal(coverLink);
});
onUnmounted(() => {
if (cover.value) URL.revokeObjectURL(cover.value);
});
console.log("tpLinkCard", props.data.insert.link_card.card_id, toRaw(props.data).insert.link_card); console.log("tpLinkCard", props.data.insert.link_card.card_id, toRaw(props.data).insert.link_card);
async function toLink(): Promise<void> { async function toLink(): Promise<void> {

View File

@@ -20,7 +20,7 @@
<div class="ab-draw-grid"> <div class="ab-draw-grid">
<div v-for="item in selectedItem" :key="item.op_id" class="ab-draw"> <div v-for="item in selectedItem" :key="item.op_id" class="ab-draw">
<div class="ab-draw-cover" @click="showImg(item)"> <div class="ab-draw-cover" @click="showImg(item)">
<img :src="item.unread_picture[Number(isAether)]" :alt="item.word_text" /> <TMiImg :src="item.unread_picture[Number(isAether)]" :alt="item.word_text" />
<div class="ab-draw-hide" /> <div class="ab-draw-hide" />
<v-icon class="ab-draw-icon">mdi-magnify</v-icon> <v-icon class="ab-draw-icon">mdi-magnify</v-icon>
</div> </div>
@@ -33,6 +33,7 @@
<ToArcBrith v-model="showOverlay" :data="current" :choice="isAether" /> <ToArcBrith v-model="showOverlay" :data="current" :choice="isAether" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import ToArcBrith from "@comp/pageArchive/to-arcBrith.vue"; import ToArcBrith from "@comp/pageArchive/to-arcBrith.vue";
import { computed, onMounted, ref, shallowRef, watch } from "vue"; import { computed, onMounted, ref, shallowRef, watch } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";

View File

@@ -2,7 +2,7 @@
<v-app-bar> <v-app-bar>
<template #prepend> <template #prepend>
<div class="post-topic-top" v-if="topicInfo"> <div class="post-topic-top" v-if="topicInfo">
<img :src="topicInfo.topic.cover" alt="cover" /> <TMiImg :src="topicInfo.topic.cover" alt="cover" :ori="true" />
<div class="post-topic-info"> <div class="post-topic-info">
<span>{{ topicInfo.topic.name }}({{ topic }})</span> <span>{{ topicInfo.topic.name }}({{ topic }})</span>
<span :title="topicInfo.topic.desc">{{ topicInfo.topic.desc }}</span> <span :title="topicInfo.topic.desc">{{ topicInfo.topic.desc }}</span>
@@ -60,6 +60,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TGameNav from "@comp/app/t-gameNav.vue"; import TGameNav from "@comp/app/t-gameNav.vue";
import TMiImg from "@comp/app/t-mi-img.vue";
import TPostCard from "@comp/app/t-postcard.vue"; import TPostCard from "@comp/app/t-postcard.vue";
import showLoading from "@comp/func/loading.js"; import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js"; import showSnackbar from "@comp/func/snackbar.js";

View File

@@ -12,7 +12,7 @@
<div class="tp-post-meta"> <div class="tp-post-meta">
<div class="mpm-forum" v-if="postData.forum" @click="toForum(postData.forum)"> <div class="mpm-forum" v-if="postData.forum" @click="toForum(postData.forum)">
<img :src="getGameIcon(postData.forum.game_id)" alt="gameIcon" /> <img :src="getGameIcon(postData.forum.game_id)" alt="gameIcon" />
<img :src="postData.forum.icon" alt="forumIcon" /> <TMiImg :src="postData.forum.icon" alt="forumIcon" :ori="true" />
<span>{{ postData.forum.name }}</span> <span>{{ postData.forum.name }}</span>
</div> </div>
<div class="mpm-item" :title="`浏览数:${postData?.stat?.view_num}`"> <div class="mpm-item" :title="`浏览数:${postData?.stat?.view_num}`">
@@ -83,6 +83,7 @@
/> />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import TPinWin from "@comp/app/t-pinWin.vue"; import TPinWin from "@comp/app/t-pinWin.vue";
import TShareBtn from "@comp/app/t-shareBtn.vue"; import TShareBtn from "@comp/app/t-shareBtn.vue";
import TSwitchTheme from "@comp/app/t-switchTheme.vue"; import TSwitchTheme from "@comp/app/t-switchTheme.vue";