mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-16 09:58:13 +08:00
♻️ 样式调整,慢慢改吧
This commit is contained in:
85
src/components/app/t-backTop.vue
Normal file
85
src/components/app/t-backTop.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-show="canTop" class="back-top" @click="handleScrollTop">
|
||||
<img src="../../assets/icons/back-top.svg" alt="back-icon" />
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// vue
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const scrollTop = ref(0); // 滚动条距离顶部的距离
|
||||
const canTop = ref(false); // 默认不显示
|
||||
|
||||
// 监听滚动事件
|
||||
function handleScroll(): void {
|
||||
scrollTop.value = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
// 超过500px显示回到顶部按钮
|
||||
canTop.value = scrollTop.value > 500;
|
||||
// 没超过500,但是到底部了,也显示回到顶部按钮
|
||||
if (!canTop.value) {
|
||||
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
|
||||
const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
|
||||
canTop.value = scrollHeight - clientHeight - scrollTop.value <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 点击回到顶部
|
||||
function handleScrollTop(): void {
|
||||
let timer = 0;
|
||||
cancelAnimationFrame(timer);
|
||||
timer = requestAnimationFrame(function fn() {
|
||||
if (scrollTop.value > 0) {
|
||||
scrollTop.value -= 50;
|
||||
document.body.scrollTop = document.documentElement.scrollTop = scrollTop.value;
|
||||
timer = requestAnimationFrame(fn);
|
||||
} else {
|
||||
cancelAnimationFrame(timer);
|
||||
canTop.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 监听滚动事件
|
||||
onMounted(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.back-top {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.back-top:hover {
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px 5px var(--back-top-shadow);
|
||||
cursor: pointer;
|
||||
transform: scale(0.9);
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.back-top img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
216
src/components/app/t-sidebar.vue
Normal file
216
src/components/app/t-sidebar.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<v-navigation-drawer :permanent="true" :rail="rail" class="tsb-box">
|
||||
<v-list v-model:opened="open" class="side-list" density="compact" :nav="true">
|
||||
<!-- 负责收缩侧边栏 -->
|
||||
<v-list-item @click="collapse()">
|
||||
<template v-if="rail" #prepend>
|
||||
<v-list-item-action>
|
||||
<v-icon>mdi-chevron-right</v-icon>
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
<template v-else #append>
|
||||
<v-list-item-action>
|
||||
<v-icon>mdi-chevron-left</v-icon>
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<!-- 菜单项 -->
|
||||
<v-list-item value="home" title="首页" :link="true" href="/">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/paimon.webp" alt="homeIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="公告" value="announcements" :link="true" href="/announcements">
|
||||
<template #prepend>
|
||||
<img src="../../assets/icons/board.svg" alt="annoIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="咨讯" value="news" :link="true" href="/news/2">
|
||||
<template #prepend>
|
||||
<img src="/platforms/mhy/mys.webp" alt="mihoyo" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="成就" value="achievements" :link="true" href="/achievements">
|
||||
<template #prepend>
|
||||
<img src="../../assets/icons/achievements.svg" alt="achievementsIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item title="原神战绩" value="record" :link="true" href="/user/record">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/userRecord.webp" alt="record" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="我的角色" value="character" :link="true" href="/user/characters">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/userAvatar.webp" alt="characters" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="深渊记录" value="abyss" :link="true" href="/user/abyss">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/userAbyss.webp" alt="abyss" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="祈愿记录" value="gacha" :link="true" href="/user/gacha">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/userGacha.webp" alt="gacha" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item v-show="appStore.devEnv" title="测试" value="test" :link="true" href="/test">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-test-tube</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-divider v-show="appStore.devEnv" />
|
||||
<v-list-group value="wiki" :fluid="true">
|
||||
<template #activator="{ props }">
|
||||
<v-list-item title="图鉴" v-bind="props">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiIcon.webp" alt="wikiIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item title="深渊数据库" value="wiki-abyss" :link="true" href="/wiki/abyss">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="abyssIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="角色图鉴" value="wiki-character" :link="true" href="/wiki/character">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAvatar.webp" alt="characterIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="武器图鉴" value="wiki-weapon" :link="true" href="/wiki/weapon">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiWeapon.webp" alt="weaponIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="GCG" value="wiki-GCG" :link="true" href="/wiki/GCG">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiGCG.webp" alt="gcgIcon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list-group>
|
||||
<div class="bottom-menu">
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<img :src="userInfo.avatar" alt="userIcon" class="side-icon" />
|
||||
</template>
|
||||
<template #default>
|
||||
<v-list-item-title>
|
||||
{{ userInfo.nickname }}
|
||||
</v-list-item-title>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :title="themeTitle" @click="switchTheme()">
|
||||
<template #prepend>
|
||||
<v-icon>
|
||||
{{ themeGet === "default" ? "mdi-weather-night" : "mdi-weather-sunny" }}
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="设置" value="config" :link="true" href="/config">
|
||||
<template #prepend>
|
||||
<img src="../../assets/icons/setting.svg" alt="setting" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// vue
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
// tauri
|
||||
import { event } from "@tauri-apps/api";
|
||||
// store
|
||||
import { useAppStore } from "../../store/modules/app";
|
||||
import { useUserStore } from "../../store/modules/user";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const userInfo = computed(() => {
|
||||
const info = userStore.getBriefInfo();
|
||||
return {
|
||||
nickname: info.nickname || "未登录",
|
||||
avatar: info.avatar || "/source/UI/defaultUser.webp",
|
||||
};
|
||||
});
|
||||
const rail = ref(appStore.sidebar.collapse);
|
||||
// theme
|
||||
const themeGet = computed({
|
||||
get() {
|
||||
return appStore.theme;
|
||||
},
|
||||
set(value: string) {
|
||||
appStore.theme = value;
|
||||
},
|
||||
});
|
||||
const themeTitle = computed(() => {
|
||||
return themeGet.value === "default" ? "夜间模式" : "日间模式";
|
||||
});
|
||||
|
||||
const open = computed({
|
||||
get() {
|
||||
return appStore.getSubmenu();
|
||||
},
|
||||
set(value: string[]) {
|
||||
appStore.sidebar.submenu.wiki = value.includes("wiki");
|
||||
},
|
||||
});
|
||||
|
||||
function collapse(): void {
|
||||
rail.value = !rail.value;
|
||||
appStore.sidebar.collapse = rail.value;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await listenOnTheme();
|
||||
});
|
||||
|
||||
async function listenOnTheme(): Promise<void> {
|
||||
await event.listen("readTheme", (e) => {
|
||||
const theme = <string>e.payload;
|
||||
themeGet.value = theme === "default" ? "default" : "dark";
|
||||
});
|
||||
}
|
||||
|
||||
async function switchTheme(): Promise<void> {
|
||||
await event.emit("readTheme", themeGet.value === "default" ? "dark" : "default");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.tsb-box {
|
||||
background: var(--app-side-bg);
|
||||
color: var(--app-side-content);
|
||||
}
|
||||
|
||||
.side-list {
|
||||
height: 100%;
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.bottom-menu {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.side-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 5px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
.side-icon-mini {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 20px;
|
||||
transform: translateX(-6px);
|
||||
}
|
||||
</style>
|
||||
68
src/components/app/t-switchTheme.vue
Normal file
68
src/components/app/t-switchTheme.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="switch-box">
|
||||
<div class="switch-btn" @click="switchTheme()">
|
||||
<v-icon style="color: var(--theme-switch-icon)">
|
||||
{{ themeGet === "default" ? "mdi-weather-night" : "mdi-weather-sunny" }}
|
||||
</v-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// vue
|
||||
import { computed, onMounted } from "vue";
|
||||
// tauri
|
||||
import { event } from "@tauri-apps/api";
|
||||
// store
|
||||
import { useAppStore } from "../../store/modules/app";
|
||||
|
||||
// store
|
||||
const appStore = useAppStore();
|
||||
// theme
|
||||
const themeGet = computed({
|
||||
get() {
|
||||
return appStore.theme;
|
||||
},
|
||||
set(value: string) {
|
||||
appStore.theme = value;
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await listenOnTheme();
|
||||
});
|
||||
|
||||
async function switchTheme(): Promise<void> {
|
||||
appStore.changeTheme();
|
||||
await event.emit("readTheme", themeGet.value);
|
||||
}
|
||||
|
||||
async function listenOnTheme(): Promise<void> {
|
||||
await event.listen("readTheme", (e) => {
|
||||
const theme = <string>e.payload;
|
||||
themeGet.value = theme === "default" ? "default" : "dark";
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.switch-box {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
border: var(--theme-switch-icon) 2px solid;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch-box:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 5px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user