208 lines
6.0 KiB
Vue
208 lines
6.0 KiB
Vue
<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>
|