🚸 部分资源释放

This commit is contained in:
目棃
2025-02-01 19:44:49 +08:00
parent ade05d8dab
commit 25fdbd8444
14 changed files with 129 additions and 144 deletions

View File

@@ -7,7 +7,9 @@
@click="emits('click')"
:class="props.class"
/>
<v-progress-circular v-else indeterminate color="primary" size="25" />
<div class="progress" v-else>
<v-progress-circular indeterminate color="primary" size="25" />
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, watch } from "vue";
@@ -22,9 +24,7 @@ type TMiImgProps = {
class?: string;
ori?: boolean;
};
type TMiImgEmits = {
(e: "click"): void;
};
type TMiImgEmits = (e: "click") => void;
const props = defineProps<TMiImgProps>();
const emits = defineEmits<TMiImgEmits>();
@@ -42,6 +42,7 @@ watch(
async () => {
if (!props.src) return;
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
localUrl.value = undefined;
const link = props.ori ? props.src : appStore.getImageUrl(props.src);
localUrl.value = await saveImgLocal(link);
},
@@ -51,3 +52,13 @@ onUnmounted(() => {
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
});
</script>
<style lang="scss" scoped>
.progress {
width: 25px;
height: 25px;
display: flex;
position: relative;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -23,7 +23,7 @@
</div>
<div class="tua-abl-skills">
<div v-for="skill in skills" :key="skill.skill_id" class="tua-abl-skill">
<img :src="skill.icon" alt="skill" />
<TMiImg :ori="true" :src="skill.icon" alt="skill" />
<span>Lv.{{ skill.level }}</span>
</div>
</div>
@@ -46,6 +46,7 @@
</template>
<script lang="ts" setup>
import TItemBox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
import TMiImg from "@comp/app/t-mi-img.vue";
import TSUserAvatar from "@Sqlite/modules/userAvatar.js";
import { computed } from "vue";

View File

@@ -6,7 +6,7 @@
class="tua-dcc-item"
:title="`${constellation.pos}命-${constellation.name}`"
>
<img :src="constellation.icon" alt="constellation" class="tua-dcc-icon" />
<TMiImg :ori="true" :src="constellation.icon" alt="constellation" class="tua-dcc-icon" />
<div v-if="!constellation.is_actived" class="tua-dcc-lock">
<v-icon size="10px" color="var(--tgc-od-white)">mdi-lock</v-icon>
</div>
@@ -14,6 +14,8 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
type TuaDcConstellationsProps = { modelValue: Array<TGApp.Game.Avatar.Constellation> };
const props = defineProps<TuaDcConstellationsProps>();

View File

@@ -16,7 +16,7 @@
v-if="props.modelValue === false"
class="empty"
/>
<img :src="props.modelValue.icon" :alt="props.modelValue.name" v-else />
<TMiImg :ori="true" :src="props.modelValue.icon" :alt="props.modelValue.name" v-else />
</div>
</div>
<div class="tua-dcr-right">
@@ -59,6 +59,7 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import { computed } from "vue";
import { useUserStore } from "@/store/modules/user.js";
@@ -188,8 +189,11 @@ function getPropSubStyle(
.tua-dcr-icon {
position: relative;
display: flex;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
img {
width: 100%;

View File

@@ -6,7 +6,7 @@
:title="skill.name"
class="tua-dct-item"
>
<img :src="skill.icon" alt="talent" class="tua-dct-icon" />
<TMiImg :ori="true" :src="skill.icon" alt="talent" class="tua-dct-icon" />
<div v-if="!skill.is_unlock" class="tua-dct-lock">
<v-icon size="10px" color="var(--tgc-od-white)">mdi-lock</v-icon>
</div>
@@ -17,6 +17,8 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
type TuaDcTalentsProps = { modelValue: Array<TGApp.Game.Avatar.Skill> };
const props = defineProps<TuaDcTalentsProps>();

View File

@@ -1,6 +1,8 @@
<template>
<div class="tua-dc-container">
<img :src="avatar" class="tua-dc-avatar" alt="avatar" />
<div class="tua-dc-avatar">
<TMiImg :src="props.modelValue.avatar.image" :ori="true" alt="avatar" />
</div>
<v-btn
class="tua-dc-share"
prepend-icon="mdi-share-variant"
@@ -73,9 +75,10 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import TSUserAvatar from "@Sqlite/modules/userAvatar.js";
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import { computed, onMounted, ref } from "vue";
import TuaDcConstellations from "./tua-dc-constellations.vue";
import TuaDcProp from "./tua-dc-prop.vue";
@@ -84,7 +87,7 @@ import TuaDcTalents from "./tua-dc-talents.vue";
import TuaDcWeapon from "./tua-dc-weapon.vue";
import { useUserStore } from "@/store/modules/user.js";
import { generateShareImg, saveImgLocal } from "@/utils/TGShare.js";
import { generateShareImg } from "@/utils/TGShare.js";
type fixedLenArr<T, N extends number> = [T, ...Array<T>] & { length: N };
type RelicList = fixedLenArr<TGApp.Game.Avatar.Relic | false, 5>;
@@ -107,36 +110,13 @@ const propMain = computed<Array<TGApp.Game.Avatar.PropMapItem | false>>(() =>
);
const bg = ref<string>("/WIKI/nameCard/profile/原神·印象.webp");
const avatar = ref<string>(props.modelValue.avatar.image);
const loading = ref<boolean>(false);
onMounted(async () => {
await loadData();
});
onUnmounted(() => {
if (avatar.value.startsWith("blob:")) {
URL.revokeObjectURL(avatar.value);
}
});
watch(
() => props.modelValue,
async () => {
if (avatar.value.startsWith("blob:")) {
URL.revokeObjectURL(avatar.value);
}
avatar.value = props.modelValue.avatar.image;
await loadData();
},
);
onMounted(async () => await loadData());
async function loadData(): Promise<void> {
const card = TSUserAvatar.getAvatarCard(props.modelValue.cid);
bg.value = `url("/WIKI/nameCard/profile/${card}.webp")`;
if (!avatar.value.startsWith("blob:")) {
avatar.value = await saveImgLocal(props.modelValue.avatar.image);
}
}
async function share(): Promise<void> {
@@ -176,9 +156,18 @@ async function share(): Promise<void> {
.tua-dc-avatar {
position: absolute;
top: 0;
left: -80px;
left: 0;
display: flex;
width: 300px;
height: 100%;
align-items: center;
justify-content: center;
object-fit: contain;
img {
height: 100%;
object-fit: contain;
}
}
.tua-dc-rt {

View File

@@ -14,11 +14,13 @@
v-if="props.modelValue === false"
class="empty"
/>
<img :src="props.modelValue.icon" :alt="props.modelValue.name" v-else />
<TMiImg :ori="true" :src="props.modelValue.icon" :alt="props.modelValue.name" v-else />
</div>
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import { useUserStore } from "@/store/modules/user.js";
type TuaRelicBoxProps = { modelValue: TGApp.Game.Avatar.Relic | false; position: number };
@@ -71,8 +73,11 @@ function getRelicTitle(): string {
.tua-relic-icon {
position: relative;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
img {
width: 100%;

View File

@@ -1,7 +1,7 @@
<template>
<div class="duc-dolb-box">
<div
v-for="constellation in constellations"
v-for="constellation in props.modelValue"
:key="constellation.pos"
:title="constellation.name"
class="duc-dolb-item"
@@ -9,48 +9,18 @@
<div v-if="!constellation.is_actived" class="duc-dolb-lock">
<v-icon color="white">mdi-lock</v-icon>
</div>
<img class="duc-dolb-icon" :src="constellation.icon" alt="constellation" />
<div class="duc-dolb-icon">
<TMiImg :ori="true" :src="constellation.icon" alt="constellation" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef, watch } from "vue";
import { saveImgLocal } from "@/utils/TGShare.js";
import TMiImg from "@comp/app/t-mi-img.vue";
type DucDetailOlbProps = { modelValue: Array<TGApp.Game.Avatar.Constellation> };
const props = defineProps<DucDetailOlbProps>();
const constellations = shallowRef<Array<TGApp.Game.Avatar.Constellation>>([]);
async function loadData() {
const tempConstellations = JSON.parse(JSON.stringify(props.modelValue));
for (const constellation of tempConstellations) {
if (constellation.icon.startsWith("blob:")) return;
constellation.icon = await saveImgLocal(constellation.icon);
}
constellations.value = tempConstellations;
}
onMounted(async () => await loadData());
watch(
() => props.modelValue,
async () => {
for (const constellation of constellations.value) {
if (constellation.icon.startsWith("blob:")) {
URL.revokeObjectURL(constellation.icon);
}
}
await loadData();
},
);
onUnmounted(() => {
for (const constellation of constellations.value) {
if (constellation.icon.startsWith("blob:")) {
URL.revokeObjectURL(constellation.icon);
}
}
});
</script>
<style>
.duc-dolb-box {
@@ -75,6 +45,7 @@ onUnmounted(() => {
.duc-dolb-lock {
position: absolute;
z-index: 2;
display: flex;
width: 54px;
height: 54px;
@@ -88,9 +59,20 @@ onUnmounted(() => {
}
.duc-dolb-icon {
position: relative;
z-index: 1;
display: flex;
width: 50px;
height: 50px;
align-items: center;
justify-content: center;
padding: 5px;
border-radius: 50%;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
</style>

View File

@@ -2,54 +2,24 @@
<div class="duc-dort-box">
<div
:title="talent.name"
v-for="talent in talents"
v-for="talent in props.modelValue"
:key="talent.skill_id"
class="duc-dort-item"
>
<span>{{ talent.name }}</span>
<img :src="talent.icon" alt="talent" />
<div class="duc-dort-icon">
<TMiImg :ori="true" :src="talent.icon" alt="talent" />
</div>
<span>Lv.{{ talent.level === 0 ? 1 : talent.level }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, shallowRef, watch } from "vue";
import { saveImgLocal } from "@/utils/TGShare.js";
import TMiImg from "@comp/app/t-mi-img.vue";
type DucDetailOrtProps = { modelValue: Array<TGApp.Game.Avatar.Skill> };
const props = defineProps<DucDetailOrtProps>();
const talents = shallowRef<Array<TGApp.Game.Avatar.Skill>>([]);
async function loadData(): Promise<void> {
const tempTalent = JSON.parse(JSON.stringify(props.modelValue));
for (const talent of tempTalent) {
if (talent.icon.startsWith("blob:")) return;
talent.icon = await saveImgLocal(talent.icon);
}
talents.value = tempTalent;
}
onMounted(async () => await loadData());
watch(
() => props.modelValue,
async () => {
for (const talent of talents.value) {
if (talent.icon.startsWith("blob:")) {
URL.revokeObjectURL(talent.icon);
}
}
await loadData();
},
);
onUnmounted(() => {
for (const talent of talents.value) {
if (talent.icon.startsWith("blob:")) {
URL.revokeObjectURL(talent.icon);
}
}
});
</script>
<style lang="css" scoped>
.duc-dort-box {
@@ -61,17 +31,28 @@ onUnmounted(() => {
.duc-dort-item {
display: flex;
justify-content: flex-end;
column-gap: 10px;
column-gap: 8px;
}
.duc-dort-item img {
width: 40px;
height: 40px;
padding: 5px;
.duc-dort-icon {
position: relative;
display: flex;
width: 48px;
height: 48px;
box-sizing: border-box;
align-items: center;
justify-content: center;
padding: 4px;
border-radius: 50%;
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
background: rgba(0 0 0 /40%);
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.duc-dort-item span {
@@ -83,9 +64,4 @@ onUnmounted(() => {
font-size: 16px;
text-shadow: 0 0 5px rgba(0 0 0/40%);
}
.duc-dort-item :nth-last-child(1) {
width: 48px;
justify-content: flex-start;
}
</style>

View File

@@ -7,7 +7,7 @@
<img :src="`/icon/bg/${props.modelValue.rarity}-Star.webp`" alt="bg" />
</div>
<div v-if="props.modelValue" class="duc-dr-icon">
<img :src="props.modelValue.icon" alt="relic" />
<TMiImg :ori="true" :src="props.modelValue.icon" alt="relic" />
</div>
<div v-if="props.modelValue !== false" class="duc-dr-level">
{{ props.modelValue.level }}
@@ -15,6 +15,7 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import { computed } from "vue";
type ducDetailRelicProps = { modelValue: TGApp.Game.Avatar.Relic | false; pos: number };
@@ -61,8 +62,11 @@ const relicBg = computed<string>(() => {
position: absolute;
top: 0;
left: 0;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
}
.duc-dr-icon img {

View File

@@ -1,14 +1,16 @@
<template>
<div class="tuc-dc-box">
<div v-if="!modelValue.is_actived" class="tuc-dc-lock">
<v-icon color="white"> mdi-lock </v-icon>
<v-icon color="white"> mdi-lock</v-icon>
</div>
<div class="tuc-dc-icon">
<img :src="modelValue.icon" alt="constellation" />
<TMiImg :ori="true" :src="modelValue.icon" alt="constellation" />
</div>
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
defineProps<{ modelValue: TGApp.Game.Avatar.Constellation }>();
</script>
<style lang="css" scoped>
@@ -34,8 +36,11 @@ defineProps<{ modelValue: TGApp.Game.Avatar.Constellation }>();
}
.tuc-dc-icon {
display: flex;
width: 54px;
height: 54px;
align-items: center;
justify-content: center;
padding: 3px;
border: 1px solid rgb(0 0 0/20%);
border-radius: 50%;

View File

@@ -1,6 +1,8 @@
<template>
<div class="tuc-do-box">
<img :src="bg" alt="role" class="tuc-do-bg" />
<div class="tuc-do-bg">
<TMiImg :ori="true" :src="bg" alt="role" />
</div>
<div class="tuc-do-show">
<div class="tuc-do-main">
<div class="tuc-do-left">
@@ -72,6 +74,7 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import { computed, onMounted, ref, shallowRef, watch } from "vue";
import TucDetailConstellation from "./tuc-detail-constellation.vue";
@@ -160,12 +163,21 @@ function switchBg(): void {
position: absolute;
top: 0;
left: 0;
display: flex;
width: 100%;
max-height: 100%;
align-items: center;
justify-content: center;
border-radius: 5px;
margin: 0 auto;
object-fit: v-bind(bgFit);
transform: translateY(v-bind(bgTransY));
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.tuc-do-costume {

View File

@@ -7,11 +7,13 @@
<img :src="`/icon/bg/${modelValue.rarity}-Star.webp`" alt="bg" />
</div>
<div v-if="modelValue" class="tuc-dr-icon">
<img :src="modelValue.icon" alt="relic" />
<TMiImg :ori="true" :src="modelValue.icon" alt="relic" />
</div>
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
defineProps<{ modelValue: TGApp.Game.Avatar.Relic | false; pos: number }>();
</script>
<style lang="css" scoped>
@@ -45,8 +47,11 @@ defineProps<{ modelValue: TGApp.Game.Avatar.Relic | false; pos: number }>();
position: absolute;
top: 0;
left: 0;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
}
.tuc-dr-icon img {

View File

@@ -2,11 +2,12 @@
<div class="tp-avatar-box">
<div v-if="props.position === 'right'" class="tpa-text">
<div>{{ props.data.nickname }}</div>
<div :title="getAuthorDesc()">{{ getAuthorDesc() }}</div>
<div :title="authorDesc">{{ authorDesc }}</div>
</div>
<div class="tpa-img">
<img :src="avatarUrl" alt="avatar" class="tpa-icon" v-if="avatarUrl" />
<img
<TMiImg :ori="true" :src="props.data.avatar_url" alt="avatar" class="tpa-icon" />
<TMiImg
:ori="true"
:src="props.data.pendant"
alt="pendant"
class="tpa-pendant"
@@ -23,36 +24,22 @@
</div>
<div v-if="props.position === 'left'" class="tpa-text">
<div>{{ props.data.nickname }}</div>
<div :title="getAuthorDesc()">{{ getAuthorDesc() }}</div>
<div :title="authorDesc">{{ authorDesc }}</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from "vue";
import { useAppStore } from "@/store/modules/app.js";
import { saveImgLocal } from "@/utils/TGShare.js";
import TMiImg from "@comp/app/t-mi-img.vue";
import { computed } from "vue";
type TpAvatarProps = { data: TGApp.Plugins.Mys.User.Post; position: "left" | "right" };
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 {
const authorDesc = computed<string>(() => {
if (props.data.certification.label !== "") return props.data.certification.label;
return props.data.introduce;
}
});
const levelColor = computed<string>(() => {
const level = props.data.level_exp.level;