208 lines
6.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<n-config-provider :theme-overrides="themeOverrides" class="h-full">
<div class="relative flex h-full flex-col">
<n-layout class="h-full" has-sider>
<n-layout-sider
v-if="!isSignupFlow && !$isMobile && !isHideSidebar && $session.user"
bordered
collapse-mode="width"
:collapsed-width="64"
:width="240"
v-model:collapsed="sidebarCollapsed"
:show-trigger="true"
:responsive="true"
:breakpoint="768"
class="app-sidebar-sider"
@collapse="sidebarCollapsed = true"
@expand="sidebarCollapsed = false"
>
<AppSidebar :collapsed="sidebarCollapsed" />
</n-layout-sider>
<n-layout class="h-full">
<div class="w-full h-full overflow-auto" id="scrollContainer">
<MobileNav
v-if="!isSignupFlow && $isMobile && !isHideSidebar && $session.user"
/>
<div
v-if="
!isSignupFlow &&
!isSiteLogin &&
!$session.user &&
!$route.meta.isLoginPage
"
class="border bg-red-200 px-5 py-3 text-base text-red-900"
>
You are not logged in.
<router-link to="/login" class="underline">Login</router-link> to
access dashboard.
</div>
<router-view />
</div>
</n-layout>
</n-layout>
<Toaster position="top-right" />
<component v-for="dialog in dialogs" :is="dialog" :key="dialog.id" />
</div>
</n-config-provider>
</template>
<script setup>
import { defineAsyncComponent, computed, watch, ref, provide, onMounted } from 'vue';
import { NLayout, NLayoutSider, NConfigProvider } from 'naive-ui';
import { Toaster } from 'vue-sonner';
import { dialogs } from './utils/components';
import { useRoute } from 'vue-router';
import { getTeam } from './data/team';
import { session } from './data/session.js';
const AppSidebar = defineAsyncComponent(
() => import('./components/AppSidebar.vue'),
);
const MobileNav = defineAsyncComponent(
() => import('./components/MobileNav.vue'),
);
const route = useRoute();
const team = getTeam();
// Naive UI 主题配置 - 使用标准方式配置 tooltip
const themeOverrides = {
Tooltip: {
color: '#fafafa',
textColor: '#4a5568',
padding: '6px 12px',
borderRadius: '8px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
// 注意Naive UI 的 theme-overrides 可能不直接支持箭头颜色
// 箭头颜色需要通过 CSS 覆盖
},
};
// 侧边栏折叠状态
const sidebarCollapsed = ref(false);
// 响应式:在移动端默认折叠
onMounted(() => {
const checkMobile = () => {
if (window.innerWidth < 768) {
sidebarCollapsed.value = true;
}
};
checkMobile();
window.addEventListener('resize', checkMobile);
});
const isHideSidebar = computed(() => {
const alwaysHideSidebarRoutes = [
'Site Login',
'SignupLoginToSite',
'SignupSetup',
];
const alwaysHideSidebarPaths = ['/dashboard/site-login'];
if (!session.user) return false;
if (
alwaysHideSidebarRoutes.includes(route.name) ||
alwaysHideSidebarPaths.includes(window.location.pathname)
)
return true;
return (
route.meta.hideSidebar && session.user && team?.pg?.hide_sidebar === true
);
});
const isSignupFlow = ref(
window.location.pathname.startsWith('/dashboard/create-site') ||
window.location.pathname.startsWith('/dashboard/setup-account') ||
window.location.pathname.startsWith('/dashboard/site-login') ||
window.location.pathname.startsWith('/dashboard/signup'),
);
const isSiteLogin = ref(window.location.pathname.endsWith('/site-login'));
watch(
() => route.name,
() => {
isSignupFlow.value =
window.location.pathname.startsWith('/dashboard/create-site') ||
window.location.pathname.startsWith('/dashboard/setup-account') ||
window.location.pathname.startsWith('/dashboard/site-login') ||
window.location.pathname.startsWith('/dashboard/signup');
},
);
provide('team', team);
provide('session', session);
</script>
<style src="../src/assets/style.css"></style>
<style>
/* 侧边栏整体样式优化 - 平滑过渡 */
.app-sidebar-sider {
background: #fafafa !important;
border-right: 1px solid rgba(0, 0, 0, 0.06) !important;
/* 使用平滑的缓动函数,避免抖动 */
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.app-sidebar-sider:not(.n-layout-sider--collapsed) {
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.04) !important;
}
/* 触发器样式优化 - 平滑过渡 */
.app-sidebar-sider .n-layout-sider-trigger {
background: #fff !important;
border: 1px solid rgba(0, 0, 0, 0.08) !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
/* 使用平滑的缓动函数,避免抖动 */
transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
width: 32px !important;
height: 32px !important;
border-radius: 50% !important;
right: -16px !important;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.app-sidebar-sider .n-layout-sider-trigger:hover {
background: #f5f5f5 !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}
.app-sidebar-sider .n-layout-sider-trigger:active {
background: #eeeeee !important;
}
.app-sidebar-sider .n-layout-sider-trigger .n-base-icon {
color: #666;
font-size: 14px;
/* 移除旋转动画,避免抖动 */
transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
/* 确保侧边栏内容正确显示 */
.app-sidebar-sider .n-layout-sider-scroll-container {
height: 100%;
overflow: hidden;
/* 添加内容过渡效果 */
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
/* 确保 n-config-provider 占满高度 */
.n-config-provider {
height: 100%;
display: flex;
flex-direction: column;
}
/* 确保侧边栏占满高度 */
.app-sidebar-sider {
height: 100% !important;
min-height: 100vh !important;
}
</style>