mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-08 15:13:28 +08:00
统一顶栏及侧边栏各项ui风格
This commit is contained in:
@@ -18,10 +18,12 @@ const { name: themeName, global: globalTheme } = useTheme()
|
||||
|
||||
const savedTheme = ref(localStorage.getItem('theme') ?? themeName)
|
||||
|
||||
const { state: currentThemeName, next: getNextThemeName } = useCycleList(
|
||||
props.themes.map(t => t.name),
|
||||
{ initialValue: savedTheme.value },
|
||||
)
|
||||
const currentThemeName = ref(savedTheme.value)
|
||||
const getNextThemeName = () => {
|
||||
const currentIndex = props.themes.findIndex(t => t.name === currentThemeName.value)
|
||||
const nextIndex = (currentIndex + 1) % props.themes.length
|
||||
return props.themes[nextIndex].name
|
||||
}
|
||||
|
||||
const $toast = useToast()
|
||||
|
||||
@@ -187,25 +189,47 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VMenu v-if="props.themes">
|
||||
<VMenu v-if="props.themes" class="theme-menu">
|
||||
<template v-slot:activator="{ props }">
|
||||
<IconBtn v-bind="props">
|
||||
<VIcon :icon="getThemeIcon" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
<VList>
|
||||
<VListItem v-for="theme in props.themes" :key="theme.name" @click="changeTheme(theme.name)">
|
||||
<template #prepend>
|
||||
<VIcon :icon="theme.icon" />
|
||||
</template>
|
||||
<VListItemTitle>{{ theme.title }}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem @click="cssDialog = true">
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-palette" />
|
||||
</template>
|
||||
<VListItemTitle>自定义</VListItemTitle>
|
||||
</VListItem>
|
||||
<VList elevation="0" class="theme-switcher-list">
|
||||
<div class="theme-switcher-header px-3 py-3 mb-2">
|
||||
<div class="text-primary text-h6 font-weight-medium">主题选择</div>
|
||||
</div>
|
||||
|
||||
<div class="theme-switcher-options px-2">
|
||||
<VListItem
|
||||
v-for="theme in props.themes"
|
||||
:key="theme.name"
|
||||
@click="changeTheme(theme.name)"
|
||||
class="theme-option"
|
||||
:class="{ 'theme-option-active': currentThemeName === theme.name }"
|
||||
>
|
||||
<template #prepend>
|
||||
<div class="theme-icon-wrapper">
|
||||
<VIcon :icon="theme.icon" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>{{ theme.title }}</VListItemTitle>
|
||||
<template #append v-if="currentThemeName === theme.name">
|
||||
<VIcon icon="mdi-check" color="primary" size="small" />
|
||||
</template>
|
||||
</VListItem>
|
||||
|
||||
<VDivider class="my-2" />
|
||||
|
||||
<VListItem @click="cssDialog = true" class="theme-option custom-theme-option">
|
||||
<template #prepend>
|
||||
<div class="theme-icon-wrapper custom-theme-icon">
|
||||
<VIcon icon="mdi-palette" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>自定义主题</VListItemTitle>
|
||||
</VListItem>
|
||||
</div>
|
||||
</VList>
|
||||
</VMenu>
|
||||
<!-- 自定义 CSS -- -->
|
||||
@@ -232,18 +256,68 @@ onMounted(() => {
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
<style lang="sass">
|
||||
// Theme transition
|
||||
.app-copy
|
||||
position: fixed !important
|
||||
z-index: -1 !important
|
||||
pointer-events: none !important
|
||||
contain: size style !important
|
||||
overflow: clip !important
|
||||
<style lang="scss">
|
||||
.theme-switcher-header {
|
||||
border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.05);
|
||||
}
|
||||
|
||||
.app-transition
|
||||
--clip-size: 0
|
||||
--clip-pos: 0 0
|
||||
clip-path: circle(var(--clip-size) at var(--clip-pos))
|
||||
transition: clip-path .35s ease-out
|
||||
.theme-switcher-options {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.theme-option {
|
||||
border-radius: 8px;
|
||||
margin: 4px 0;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--v-theme-primary), 0.04);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
&.theme-option-active {
|
||||
background-color: rgba(var(--v-theme-primary), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.theme-icon-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(var(--v-theme-primary), 0.08);
|
||||
margin-right: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
.v-icon {
|
||||
color: rgba(var(--v-theme-primary), 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-theme-icon {
|
||||
background: linear-gradient(135deg, rgba(var(--v-theme-primary), 0.15), rgba(var(--v-theme-info), 0.15));
|
||||
|
||||
.v-icon {
|
||||
color: rgba(var(--v-theme-primary), 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
// Theme transition
|
||||
.app-copy {
|
||||
position: fixed !important;
|
||||
z-index: -1 !important;
|
||||
pointer-events: none !important;
|
||||
contain: size style !important;
|
||||
overflow: clip !important;
|
||||
}
|
||||
|
||||
.app-transition {
|
||||
--clip-size: 0;
|
||||
--clip-pos: 0 0;
|
||||
clip-path: circle(var(--clip-size) at var(--clip-pos));
|
||||
transition: clip-path .35s ease-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,6 +18,9 @@ $header: ".layout-navbar";
|
||||
// Add transition
|
||||
#{$header} {
|
||||
transition: padding 0.2s ease, background-color 0.18s ease;
|
||||
@extend %default-layout-vertical-nav-scrolled-sticky-elevated-nav;
|
||||
@extend %default-layout-vertical-nav-floating-navbar-and-sticky-elevated-navbar-scrolled;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
// If navbar is contained => Add border radius to header
|
||||
@@ -27,21 +30,20 @@ $header: ".layout-navbar";
|
||||
}
|
||||
}
|
||||
|
||||
// Scrolled styles for sticky navbar
|
||||
.navbar-blur#{$header} {
|
||||
@extend %blurry-bg;
|
||||
}
|
||||
|
||||
// Scrolled styles for sticky navbar (保留原有逻辑,但现在默认就会显示)
|
||||
@at-root {
|
||||
/* ℹ️ This html selector with not selector is required when:
|
||||
dialog is opened and window don't have any scroll. This removes window-scrolled class from layout and out style broke
|
||||
*/
|
||||
html.v-overlay-scroll-blocked:not([style*="--v-body-scroll-y: 0px;"]) .layout-navbar-fixed,
|
||||
&.window-scrolled.layout-navbar-fixed {
|
||||
|
||||
#{$header} {
|
||||
@extend %default-layout-vertical-nav-scrolled-sticky-elevated-nav;
|
||||
@extend %default-layout-vertical-nav-floating-navbar-and-sticky-elevated-navbar-scrolled;
|
||||
// 原有样式已在上面默认应用
|
||||
}
|
||||
|
||||
.navbar-blur#{$header} {
|
||||
@extend %blurry-bg;
|
||||
// 原有样式已在上面默认应用
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,19 +58,12 @@ $header: ".layout-navbar";
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.layout-navbar-fixed) {
|
||||
#{$header} {
|
||||
margin-block-start: variables.$vertical-nav-floating-navbar-top;
|
||||
}
|
||||
}
|
||||
|
||||
#{$header} {
|
||||
@if variables.$layout-vertical-nav-navbar-is-contained {
|
||||
border-radius: variables.$default-layout-with-vertical-nav-navbar-footer-roundness;
|
||||
}
|
||||
|
||||
background-color: rgb(var(--v-theme-surface));
|
||||
|
||||
@extend %default-layout-vertical-nav-floating-navbar-and-sticky-elevated-navbar-scrolled;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ $theme-colors-name: (
|
||||
$default-layout-with-vertical-nav-navbar-footer-roundness: 10px !default;
|
||||
|
||||
// 👉 Vertical nav
|
||||
$vertical-nav-background-color-rgb: var(--v-theme-background) !default;
|
||||
$vertical-nav-background-color-rgb: var(--v-theme-surface) !default;
|
||||
$vertical-nav-background-color: rgb(#{$vertical-nav-background-color-rgb}) !default;
|
||||
|
||||
// ℹ️ This is used to keep consistency between nav items and nav header left & right margin
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
background-color: variables.$vertical-nav-background-color;
|
||||
background-color: rgb(var(--v-theme-surface)) !important;
|
||||
|
||||
// 👉 Nav header
|
||||
.nav-header {
|
||||
@@ -60,9 +60,9 @@
|
||||
z-index: 1;
|
||||
background:
|
||||
linear-gradient(
|
||||
rgb(#{variables.$vertical-nav-background-color-rgb}) 5%,
|
||||
rgba(#{variables.$vertical-nav-background-color-rgb}, 75%) 45%,
|
||||
rgba(#{variables.$vertical-nav-background-color-rgb}, 20%) 80%,
|
||||
rgb(var(--v-theme-surface)) 5%,
|
||||
rgba(var(--v-theme-surface), 75%) 45%,
|
||||
rgba(var(--v-theme-surface), 20%) 80%,
|
||||
transparent
|
||||
);
|
||||
block-size: 55px;
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
%default-layout-vertical-nav-scrolled-sticky-elevated-nav {
|
||||
background-color: rgb(var(--v-theme-surface));
|
||||
border-radius: 12px;
|
||||
margin: 8px 8px 8px 0;
|
||||
}
|
||||
|
||||
%default-layout-vertical-nav-floating-navbar-and-sticky-elevated-navbar-scrolled {
|
||||
@include mixins.elevation(variables.$vertical-nav-navbar-elevation);
|
||||
box-shadow: 0 4px 16px rgba(var(--v-theme-on-surface), 0.08);
|
||||
|
||||
// If navbar is contained => Squeeze navbar content on scroll
|
||||
@if variables.$layout-vertical-nav-navbar-is-contained {
|
||||
|
||||
17
src/@core/scss/template/_placeholders.scss
Normal file
17
src/@core/scss/template/_placeholders.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
// 更新顶栏浮动风格的样式,增强豆腐块效果
|
||||
%default-layout-vertical-nav-floating-navbar-and-sticky-elevated-navbar-scrolled {
|
||||
padding-block: 0.7rem;
|
||||
background-color: rgb(var(--v-theme-surface));
|
||||
box-shadow: 0 4px 16px rgba(var(--v-theme-on-surface), 0.06) !important;
|
||||
border-radius: 12px;
|
||||
margin: 8px 8px 0 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// 更新毛玻璃背景效果
|
||||
%blurry-bg {
|
||||
background-color: rgba(var(--v-theme-surface), 0.9) !important;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
Reference in New Issue
Block a user