2023-07-06 13:02:52 +08:00
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { computed } from "vue"
|
|
|
|
|
import { storeToRefs } from "pinia"
|
|
|
|
|
import { useAppStore } from "@/store/modules/app"
|
|
|
|
|
import { useSettingsStore } from "@/store/modules/settings"
|
|
|
|
|
import { AppMain, NavigationBar, Sidebar, TagsView } from "./components"
|
2024-02-06 13:39:56 +08:00
|
|
|
|
import { useDevice } from "@/hooks/useDevice"
|
2023-07-06 13:02:52 +08:00
|
|
|
|
|
2024-02-06 13:39:56 +08:00
|
|
|
|
const { isMobile } = useDevice()
|
2023-07-06 13:02:52 +08:00
|
|
|
|
const appStore = useAppStore()
|
|
|
|
|
const settingsStore = useSettingsStore()
|
|
|
|
|
const { showTagsView, fixedHeader } = storeToRefs(settingsStore)
|
|
|
|
|
|
|
|
|
|
/** 定义计算属性 layoutClasses,用于控制布局的类名 */
|
|
|
|
|
const layoutClasses = computed(() => {
|
|
|
|
|
return {
|
|
|
|
|
hideSidebar: !appStore.sidebar.opened,
|
|
|
|
|
openSidebar: appStore.sidebar.opened,
|
|
|
|
|
withoutAnimation: appStore.sidebar.withoutAnimation,
|
2024-02-06 13:39:56 +08:00
|
|
|
|
mobile: isMobile.value
|
2023-07-06 13:02:52 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/** 用于处理点击 mobile 端侧边栏遮罩层的事件 */
|
|
|
|
|
const handleClickOutside = () => {
|
|
|
|
|
appStore.closeSidebar(false)
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div :class="layoutClasses" class="app-wrapper">
|
|
|
|
|
<!-- mobile 端侧边栏遮罩层 -->
|
|
|
|
|
<div v-if="layoutClasses.mobile && layoutClasses.openSidebar" class="drawer-bg" @click="handleClickOutside" />
|
|
|
|
|
<!-- 左侧边栏 -->
|
|
|
|
|
<Sidebar class="sidebar-container" />
|
|
|
|
|
<!-- 主容器 -->
|
|
|
|
|
<div :class="{ hasTagsView: showTagsView }" class="main-container">
|
|
|
|
|
<!-- 头部导航栏和标签栏 -->
|
|
|
|
|
<div :class="{ 'fixed-header': fixedHeader }" class="layout-header">
|
|
|
|
|
<NavigationBar />
|
|
|
|
|
<TagsView v-show="showTagsView" />
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 页面主体内容 -->
|
|
|
|
|
<AppMain class="app-main" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
@import "@/styles/mixins.scss";
|
|
|
|
|
$transition-time: 0.35s;
|
|
|
|
|
|
|
|
|
|
.app-wrapper {
|
2023-09-07 13:43:23 +08:00
|
|
|
|
@extend %clearfix;
|
2023-07-06 13:02:52 +08:00
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-bg {
|
2024-03-28 21:34:24 +08:00
|
|
|
|
background-color: rgba(0, 0, 0, 0.3);
|
2023-07-06 13:02:52 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
top: 0;
|
|
|
|
|
height: 100%;
|
|
|
|
|
position: absolute;
|
|
|
|
|
z-index: 999;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-container {
|
2023-07-19 09:05:55 +08:00
|
|
|
|
background-color: var(--v3-sidebar-menu-bg-color);
|
2023-07-06 13:02:52 +08:00
|
|
|
|
transition: width $transition-time;
|
|
|
|
|
width: var(--v3-sidebar-width) !important;
|
|
|
|
|
height: 100%;
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
z-index: 1001;
|
|
|
|
|
overflow: hidden;
|
2024-03-29 21:14:11 +08:00
|
|
|
|
border-right: var(--v3-sidebar-border-right);
|
2023-07-06 13:02:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.main-container {
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
transition: margin-left $transition-time;
|
|
|
|
|
margin-left: var(--v3-sidebar-width);
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fixed-header {
|
2023-09-05 09:45:10 +08:00
|
|
|
|
position: fixed !important;
|
2023-07-06 13:02:52 +08:00
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
z-index: 9;
|
|
|
|
|
width: calc(100% - var(--v3-sidebar-width));
|
|
|
|
|
transition: width $transition-time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.layout-header {
|
2023-09-05 09:45:10 +08:00
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 9;
|
2024-03-28 21:34:24 +08:00
|
|
|
|
background-color: var(--v3-header-bg-color);
|
|
|
|
|
box-shadow: var(--v3-header-box-shadow);
|
|
|
|
|
border-bottom: var(--v3-header-border-bottom);
|
2023-07-06 13:02:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-main {
|
|
|
|
|
min-height: calc(100vh - var(--v3-navigationbar-height));
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fixed-header + .app-main {
|
|
|
|
|
padding-top: var(--v3-navigationbar-height);
|
|
|
|
|
height: 100vh;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hasTagsView {
|
|
|
|
|
.app-main {
|
|
|
|
|
min-height: calc(100vh - var(--v3-header-height));
|
|
|
|
|
}
|
|
|
|
|
.fixed-header + .app-main {
|
|
|
|
|
padding-top: var(--v3-header-height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hideSidebar {
|
|
|
|
|
.sidebar-container {
|
|
|
|
|
width: var(--v3-sidebar-hide-width) !important;
|
|
|
|
|
}
|
|
|
|
|
.main-container {
|
|
|
|
|
margin-left: var(--v3-sidebar-hide-width);
|
|
|
|
|
}
|
|
|
|
|
.fixed-header {
|
|
|
|
|
width: calc(100% - var(--v3-sidebar-hide-width));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 适配 mobile 端
|
|
|
|
|
.mobile {
|
|
|
|
|
.sidebar-container {
|
|
|
|
|
transition: transform $transition-time;
|
|
|
|
|
width: var(--v3-sidebar-width) !important;
|
|
|
|
|
}
|
|
|
|
|
.main-container {
|
|
|
|
|
margin-left: 0px;
|
|
|
|
|
}
|
|
|
|
|
.fixed-header {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
&.openSidebar {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
}
|
|
|
|
|
&.hideSidebar {
|
|
|
|
|
.sidebar-container {
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transition-duration: 0.3s;
|
|
|
|
|
transform: translate3d(calc(0px - var(--v3-sidebar-width)), 0, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.withoutAnimation {
|
|
|
|
|
.sidebar-container,
|
|
|
|
|
.main-container {
|
|
|
|
|
transition: none;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|