mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-14 09:38:13 +08:00
🐛 修复一堆bug
This commit is contained in:
@@ -23,10 +23,10 @@ const themeGet = computed({
|
||||
appStore.theme = value;
|
||||
},
|
||||
});
|
||||
let themeListener: UnlistenFn;
|
||||
let themeListener: UnlistenFn | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
themeListener = listenOnTheme();
|
||||
onMounted(async () => {
|
||||
themeListener = await listenOnTheme();
|
||||
});
|
||||
|
||||
async function switchTheme(): Promise<void> {
|
||||
@@ -34,14 +34,19 @@ async function switchTheme(): Promise<void> {
|
||||
await event.emit("readTheme", themeGet.value);
|
||||
}
|
||||
|
||||
function listenOnTheme(): UnlistenFn {
|
||||
return event.listen("readTheme", (e: Event<string>) => {
|
||||
async function listenOnTheme(): Promise<UnlistenFn> {
|
||||
return await event.listen("readTheme", (e: Event<string>) => {
|
||||
const theme = e.payload;
|
||||
themeGet.value = theme === "default" ? "default" : "dark";
|
||||
});
|
||||
}
|
||||
|
||||
onUnmounted(() => themeListener());
|
||||
onUnmounted(() => {
|
||||
if (themeListener !== null) {
|
||||
themeListener();
|
||||
themeListener = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.switch-box {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="20px">
|
||||
<TOverlay v-model="visible" :hide="true" :to-click="onCancel" blur-val="20px">
|
||||
<div class="tolc-box">
|
||||
<div class="tolc-title">兑换码</div>
|
||||
<v-list-item v-for="(item, index) in props.data" :key="index">
|
||||
@@ -34,10 +34,7 @@ interface ToLiveCodeProps {
|
||||
|
||||
type ToLiveCodeEmits = (e: "update:modelValue", value: boolean) => void;
|
||||
|
||||
const props = withDefaults(defineProps<ToLiveCodeProps>(), {
|
||||
data: <TGApp.BBS.Navigator.CodeData[]>[],
|
||||
modelValue: false,
|
||||
});
|
||||
const props = withDefaults(defineProps<ToLiveCodeProps>(), { modelValue: false });
|
||||
const emits = defineEmits<ToLiveCodeEmits>();
|
||||
|
||||
const visible = computed<boolean>({
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
<div class="tp-video-box">
|
||||
<!-- todo https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html#%E8%A7%86%E9%A2%91%E4%BC%B4%E9%9F%B3%E9%9F%B3%E8%B4%A8%E4%BB%A3%E7%A0%81 -->
|
||||
<iframe
|
||||
ref="videoRef"
|
||||
class="tp-video-container"
|
||||
data-html2canvas-ignore
|
||||
:src="props.data.insert.video"
|
||||
:allowfullscreen="true"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
sandbox="allow-forms allow-same-origin allow-popups allow-presentation allow-scripts"
|
||||
:id="`tp-video-${props.data.insert.video}`"
|
||||
>
|
||||
</iframe>
|
||||
<!-- todo 优化 -->
|
||||
@@ -25,7 +24,7 @@
|
||||
// https://artplayer.org/document/library/flv.html
|
||||
// https://api.bilibili.com/x/player/playurl?avid=666064953&cid=1400018762&qn=64&otype=json
|
||||
import { window as TauriWindow } from "@tauri-apps/api";
|
||||
import { onBeforeMount, onMounted, ref } from "vue";
|
||||
import { onBeforeMount, onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
import Bili from "../../plugins/Bili/index.js";
|
||||
import { saveImgLocal } from "../../utils/TGShare.js";
|
||||
@@ -51,7 +50,15 @@ onBeforeMount(async () => {
|
||||
const url = new URL(props.data.insert.video);
|
||||
const aid = url.searchParams.get("aid") ?? undefined;
|
||||
const bvid = url.searchParams.get("bvid") ?? undefined;
|
||||
videoData.value = await Bili.video.view(aid, bvid);
|
||||
try {
|
||||
videoData.value = await Bili.video.view(aid, bvid);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
if (!videoData.value) {
|
||||
console.error("videoData is null");
|
||||
return;
|
||||
}
|
||||
const meta = videoData.value.dimension;
|
||||
if (meta.width > meta.height) {
|
||||
videoAspectRatio.value = meta.width / meta.height;
|
||||
@@ -64,7 +71,10 @@ onMounted(async () => {
|
||||
if (videoData.value && videoData.value.pic && !videoData.value.pic.startsWith("blob:")) {
|
||||
videoData.value.pic = await saveImgLocal(videoData.value.pic);
|
||||
}
|
||||
videoRef.value?.addEventListener("fullscreenchange", async () => {
|
||||
videoRef.value = <HTMLIFrameElement>(
|
||||
document.getElementById(`tp-video-${props.data.insert.video}`)
|
||||
);
|
||||
videoRef.value.addEventListener("fullscreenchange", async () => {
|
||||
if (document.fullscreenElement) {
|
||||
await TauriWindow.getCurrentWindow().setFullscreen(true);
|
||||
} else {
|
||||
@@ -86,6 +96,12 @@ function getVideoTime(): string {
|
||||
result += `${seconds.toString().padStart(2, "0")}`;
|
||||
return result;
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (videoData.value?.pic && videoData.value.pic.startsWith("blob:")) {
|
||||
URL.revokeObjectURL(videoData.value.pic);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tp-video-box {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="5px">
|
||||
<TOverlay v-model="visible" :hide="true" :to-click="onCancel" blur-val="5px">
|
||||
<div class="tpoc-box">
|
||||
<div class="tpoc-top">
|
||||
<span>{{ props.collection.collection_title }}</span>
|
||||
@@ -84,7 +84,7 @@ const posts = ref<TpoCollectionItem[]>([]);
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(async () => {
|
||||
const collectionPosts = await Mys.Collection.data(props.collection.collection_id);
|
||||
const collectionPosts = await Mys.PostCollect(props.collection.collection_id);
|
||||
const tempArr: TpoCollectionItem[] = [];
|
||||
for (const postItem of collectionPosts) {
|
||||
const post: TpoCollectionItem = {
|
||||
@@ -92,8 +92,8 @@ onMounted(async () => {
|
||||
title: postItem.post.subject,
|
||||
created: postItem.post.created_at,
|
||||
updated: postItem.post.updated_at,
|
||||
comments: postItem.stat.reply_num,
|
||||
likes: postItem.stat.like_num,
|
||||
comments: postItem.stat === null ? 0 : postItem.stat.reply_num,
|
||||
likes: postItem.stat === null ? 0 : postItem.stat.like_num,
|
||||
};
|
||||
tempArr.push(post);
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ async function selectFile(): Promise<void> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
filePath.value = file.path;
|
||||
filePath.value = file;
|
||||
try {
|
||||
const data = await readTextFile(file.path);
|
||||
const data = await readTextFile(file);
|
||||
replyData.value = JSON.parse(data);
|
||||
} catch (error) {
|
||||
showSnackbar({
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
<template>
|
||||
<div class="tuc-rb-box">
|
||||
<div class="tuc-rb-top">
|
||||
<TItemBox v-model="avatarBox" />
|
||||
<TItemBox v-model="weaponBox" />
|
||||
</div>
|
||||
<div class="tuc-rb-middle">
|
||||
<div class="tuc-rbm-fetter">
|
||||
<img src="/icon/material/105.webp" alt="fetter" />
|
||||
<span>{{ props.modelValue.fetter }}</span>
|
||||
</div>
|
||||
<div class="tuc-rbm-other">
|
||||
<span v-if="!showNameCard">
|
||||
<v-icon>mdi-lock-outline</v-icon>
|
||||
</span>
|
||||
<span v-if="props.modelValue.costume !== '[]'">
|
||||
<v-icon>mdi-tshirt-crew-outline</v-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tuc-rb-bottom">
|
||||
<div class="tuc-rbb-bg">
|
||||
<img v-if="nameCard !== false && showNameCard" :src="nameCard" alt="nameCard" />
|
||||
</div>
|
||||
<div v-show="talents.length > 0" class="tuc-rbb-content">
|
||||
<div v-for="talent in talents" :key="talent.pos" class="tuc-rbb-talent">
|
||||
<img :src="talent.icon" alt="talent" />
|
||||
<span>Lv.{{ talent.level }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
import TGSqlite from "../../plugins/Sqlite/index.js";
|
||||
import { saveImgLocal } from "../../utils/TGShare.js";
|
||||
import TItemBox from "../main/t-itembox.vue";
|
||||
|
||||
interface TucRoleBoxProps {
|
||||
modelValue: TGApp.Sqlite.Character.UserRole;
|
||||
}
|
||||
|
||||
const props = defineProps<TucRoleBoxProps>();
|
||||
const talents = ref<TGApp.Sqlite.Character.RoleTalent[]>([]);
|
||||
const showNameCard = computed(() => {
|
||||
if (props.modelValue.cid === 10000005 || props.modelValue.cid === 10000007) {
|
||||
return true;
|
||||
} else {
|
||||
return props.modelValue.fetter === 10;
|
||||
}
|
||||
});
|
||||
|
||||
const avatarBox = computed(() => {
|
||||
return {
|
||||
size: "80px",
|
||||
height: "100px",
|
||||
ltSize: "30px",
|
||||
bg: `/icon/bg/${props.modelValue.star}-Star.webp`,
|
||||
icon: `/WIKI/character/${props.modelValue.cid}.webp`,
|
||||
lt: `/icon/element/${props.modelValue.element}.webp`,
|
||||
rt: props.modelValue.activeConstellation.toString() || "0",
|
||||
rtSize: "20px",
|
||||
innerText: `Lv.${props.modelValue.level}`,
|
||||
innerHeight: 20,
|
||||
outerText: getAvatarName(),
|
||||
outerHeight: 20,
|
||||
display: <const>"outer",
|
||||
clickable: true,
|
||||
};
|
||||
});
|
||||
const weaponBox = computed(() => {
|
||||
const weapon = <TGApp.Sqlite.Character.RoleWeapon>JSON.parse(props.modelValue.weapon);
|
||||
return {
|
||||
size: "80px",
|
||||
height: "100px",
|
||||
ltSize: "30px",
|
||||
bg: `/icon/bg/${weapon.star}-Star.webp`,
|
||||
icon: `/WIKI/weapon/${weapon.id}.webp`,
|
||||
lt: `/icon/weapon/${weapon.type}.webp`,
|
||||
rt: weapon.affix.toString() || "0",
|
||||
rtSize: "20px",
|
||||
innerText: `Lv.${weapon.level}`,
|
||||
innerHeight: 20,
|
||||
outerText: weapon.name,
|
||||
outerHeight: 20,
|
||||
display: <const>"outer",
|
||||
clickable: true,
|
||||
};
|
||||
});
|
||||
const nameCard = ref<string | false>(false);
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.modelValue.cid !== 10000005 && props.modelValue.cid !== 10000007) {
|
||||
const role = await TGSqlite.getAppCharacter(props.modelValue.cid);
|
||||
nameCard.value = `/source/nameCard/profile/${role.nameCard}.webp`;
|
||||
} else {
|
||||
nameCard.value = "/source/nameCard/profile/原神·印象.webp";
|
||||
}
|
||||
if (props.modelValue.talent !== "" && props.modelValue.talent !== "[]") {
|
||||
const talentsLocal: TGApp.Sqlite.Character.RoleTalent[] = JSON.parse(props.modelValue.talent);
|
||||
talents.value = talentsLocal
|
||||
.filter((talent) => talent.max === 10)
|
||||
.sort((a, b) => a.pos - b.pos);
|
||||
talents.value.map(async (talent) => {
|
||||
talent.icon = await saveImgLocal(talent.icon);
|
||||
});
|
||||
} else {
|
||||
console.error(props.modelValue.cid, props.modelValue.name, "天赋为空");
|
||||
}
|
||||
});
|
||||
|
||||
function getAvatarName(): string {
|
||||
return props.modelValue.cid === 10000005
|
||||
? "旅行者-空"
|
||||
: props.modelValue.cid === 10000007
|
||||
? "旅行者-荧"
|
||||
: props.modelValue.name;
|
||||
}
|
||||
|
||||
// 销毁
|
||||
onUnmounted(() => {
|
||||
talents.value.forEach((talent) => {
|
||||
URL.revokeObjectURL(talent.icon);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tuc-rb-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 5px;
|
||||
border: 1px inset var(--common-shadow-2);
|
||||
border-radius: 5px;
|
||||
background: var(--box-bg-2);
|
||||
cursor: pointer;
|
||||
row-gap: 5px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.tuc-rb-top {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tuc-rb-middle {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background: var(--box-bg-3);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tuc-rbm-fetter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
.tuc-rbm-fetter img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.tuc-rbm-other {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: var(--box-text-4);
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
.tuc-rb-bottom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.tuc-rbb-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 5px;
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
backdrop-filter: blur(5px);
|
||||
background: var(--box-bg-3);
|
||||
}
|
||||
|
||||
.tuc-rbb-bg img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 5px;
|
||||
object-fit: fill;
|
||||
}
|
||||
|
||||
.tuc-rbb-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.tuc-rbb-talent {
|
||||
display: flex;
|
||||
width: 50px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
.tuc-rbb-talent :first-child {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 5px;
|
||||
border: 1px solid var(--box-bg-4);
|
||||
border-radius: 50%;
|
||||
background: var(--tgc-dark-7);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.tuc-rbb-talent :last-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
border-radius: 5px;
|
||||
background: var(--box-bg-4);
|
||||
color: var(--box-text-4);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -51,7 +51,7 @@ interface TurWorldSubProps {
|
||||
}
|
||||
|
||||
const props = defineProps<TurWorldSubProps>();
|
||||
let themeListener: UnlistenFn;
|
||||
let themeListener: UnlistenFn | null = null;
|
||||
const bg = ref<string>();
|
||||
const icon = ref<string>();
|
||||
const iconLight = ref<string>();
|
||||
@@ -59,7 +59,7 @@ const iconDark = ref<string>();
|
||||
const offer = ref<string>();
|
||||
|
||||
onMounted(async () => {
|
||||
themeListener = event.listen("readTheme", (e: Event<string>) => {
|
||||
themeListener = await event.listen("readTheme", (e: Event<string>) => {
|
||||
const theme = e.payload;
|
||||
if (theme === "dark") {
|
||||
icon.value = iconLight.value;
|
||||
@@ -81,7 +81,10 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
themeListener();
|
||||
if (themeListener !== null) {
|
||||
themeListener();
|
||||
themeListener = null;
|
||||
}
|
||||
const urlList = [iconLight.value, iconDark.value, offer.value];
|
||||
urlList.forEach((url) => {
|
||||
URL.revokeObjectURL(typeof url === "string" ? url : "");
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<template>
|
||||
<v-card class="twg-box" @click="toWiki" :title.attr="props.data.name">
|
||||
<img class="twg-border" src="/WIKI/GCG/bg/special.webp" alt="border" />
|
||||
<img class="twg-cover" :src="props.data.icon" alt="cover" />
|
||||
<div class="twg-name">
|
||||
<span>{{ props.data.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import Mys from "../../plugins/Mys/index.js";
|
||||
import { createTGWindow } from "../../utils/TGWindow.js";
|
||||
import showSnackbar from "../func/snackbar.js";
|
||||
|
||||
interface TwgCardProps {
|
||||
data: TGApp.App.GCG.WikiBriefInfo;
|
||||
}
|
||||
|
||||
const props = defineProps<TwgCardProps>();
|
||||
|
||||
async function toWiki(): Promise<void> {
|
||||
if (!props.data.contentId || props.data.contentId === 0) {
|
||||
showSnackbar({
|
||||
text: `卡牌 ${props.data.name} 暂无外部链接`,
|
||||
color: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const url = Mys.Api.Obc.replace("{contentId}", props.data.contentId.toString());
|
||||
await createTGWindow(
|
||||
url,
|
||||
"Sub_window",
|
||||
`Content_${props.data.contentId} ${props.data.name}`,
|
||||
1200,
|
||||
800,
|
||||
true,
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.twg-box {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
aspect-ratio: 7 / 12;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.twg-cover {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.twg-box:hover .twg-cover {
|
||||
transform: scale(1.2);
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.twg-border {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.twg-name {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 0 0 10px 10px;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.twg-name span {
|
||||
overflow: hidden;
|
||||
margin: 0 10px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user