| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- <script setup>
- import { computed, watchEffect } from "vue"
- import { storeToRefs } from "pinia"
- import { useAppStore } from "@/store/modules/app"
- import { useSettingsStore } from "@/store/modules/settings"
- import useResize from "./hooks/useResize"
- import { useWatermark } from "@/hooks/useWatermark"
- import { useDevice } from "@/hooks/useDevice"
- import { AppMain, NavigationBar, Sidebar, TagsView } from "./components"
- import { getCssVar, setCssVar } from "@/utils/css"
- /** Layout 布局响应式 */
- useResize()
- const { setWatermark, clearWatermark } = useWatermark()
- const { isMobile } = useDevice()
- const appStore = useAppStore()
- const settingsStore = useSettingsStore()
- const { showTagsView, showWatermark, fixedHeader } = storeToRefs(settingsStore)
- //#region 隐藏标签栏时删除其高度,是为了让 Logo 组件高度和 Header 区域高度始终一致
- const cssVarName = "--v3-tagsview-height"
- const v3TagsviewHeight = getCssVar(cssVarName)
- watchEffect(() => {
- showTagsView.value ? setCssVar(cssVarName, v3TagsviewHeight) : setCssVar(cssVarName, "0px")
- })
- //#endregion
- /** 开启或关闭系统水印 */
- watchEffect(() => {
- showWatermark.value ? setWatermark(import.meta.env.VITE_APP_TITLE) : clearWatermark()
- })
- /** 定义计算属性 layoutClasses,用于控制布局的类名 */
- const layoutClasses = computed(() => {
- return {
- hideSidebar: !appStore.sidebar.opened,
- openSidebar: appStore.sidebar.opened,
- withoutAnimation: appStore.sidebar.withoutAnimation,
- mobile: isMobile.value
- }
- })
- /** 用于处理点击 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 {
- @extend %clearfix;
- position: relative;
- width: 100%;
- }
- .drawer-bg {
- background-color: rgba(0, 0, 0, 0.3);
- width: 100%;
- top: 0;
- height: 100%;
- position: absolute;
- z-index: 999;
- }
- .sidebar-container {
- background-color: var(--v3-sidebar-menu-bg-color);
- 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;
- border-right: var(--v3-sidebar-border-right);
- }
- .main-container {
- min-height: 100%;
- transition: margin-left $transition-time;
- margin-left: var(--v3-sidebar-width);
- position: relative;
- }
- .fixed-header {
- position: fixed !important;
- top: 0;
- right: 0;
- z-index: 9;
- width: calc(100% - var(--v3-sidebar-width));
- transition: width $transition-time;
- }
- .layout-header {
- position: relative;
- z-index: 9;
- background-color: var(--v3-header-bg-color);
- box-shadow: var(--v3-header-box-shadow);
- border-bottom: var(--v3-header-border-bottom);
- }
- .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>
|