index.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <script setup>
  2. import { computed, watchEffect } from "vue"
  3. import { storeToRefs } from "pinia"
  4. import { useAppStore } from "@/store/modules/app"
  5. import { useSettingsStore } from "@/store/modules/settings"
  6. import useResize from "./hooks/useResize"
  7. import { useWatermark } from "@/hooks/useWatermark"
  8. import { useDevice } from "@/hooks/useDevice"
  9. import { AppMain, NavigationBar, Sidebar, TagsView } from "./components"
  10. import { getCssVar, setCssVar } from "@/utils/css"
  11. /** Layout 布局响应式 */
  12. useResize()
  13. const { setWatermark, clearWatermark } = useWatermark()
  14. const { isMobile } = useDevice()
  15. const appStore = useAppStore()
  16. const settingsStore = useSettingsStore()
  17. const { showTagsView, showWatermark, fixedHeader } = storeToRefs(settingsStore)
  18. //#region 隐藏标签栏时删除其高度,是为了让 Logo 组件高度和 Header 区域高度始终一致
  19. const cssVarName = "--v3-tagsview-height"
  20. const v3TagsviewHeight = getCssVar(cssVarName)
  21. watchEffect(() => {
  22. showTagsView.value ? setCssVar(cssVarName, v3TagsviewHeight) : setCssVar(cssVarName, "0px")
  23. })
  24. //#endregion
  25. /** 开启或关闭系统水印 */
  26. watchEffect(() => {
  27. showWatermark.value ? setWatermark(import.meta.env.VITE_APP_TITLE) : clearWatermark()
  28. })
  29. /** 定义计算属性 layoutClasses,用于控制布局的类名 */
  30. const layoutClasses = computed(() => {
  31. return {
  32. hideSidebar: !appStore.sidebar.opened,
  33. openSidebar: appStore.sidebar.opened,
  34. withoutAnimation: appStore.sidebar.withoutAnimation,
  35. mobile: isMobile.value
  36. }
  37. })
  38. /** 用于处理点击 mobile 端侧边栏遮罩层的事件 */
  39. const handleClickOutside = () => {
  40. appStore.closeSidebar(false)
  41. }
  42. </script>
  43. <template>
  44. <div :class="layoutClasses" class="app-wrapper">
  45. <!-- mobile 端侧边栏遮罩层 -->
  46. <div v-if="layoutClasses.mobile && layoutClasses.openSidebar" class="drawer-bg" @click="handleClickOutside" />
  47. <!-- 左侧边栏 -->
  48. <Sidebar class="sidebar-container" />
  49. <!-- 主容器 -->
  50. <div :class="{ hasTagsView: showTagsView }" class="main-container">
  51. <!-- 头部导航栏和标签栏 -->
  52. <div :class="{ 'fixed-header': fixedHeader }" class="layout-header">
  53. <NavigationBar />
  54. <TagsView v-show="showTagsView" />
  55. </div>
  56. <!-- 页面主体内容 -->
  57. <AppMain class="app-main" />
  58. </div>
  59. </div>
  60. </template>
  61. <style lang="scss" scoped>
  62. @import "@/styles/mixins.scss";
  63. $transition-time: 0.35s;
  64. .app-wrapper {
  65. @extend %clearfix;
  66. position: relative;
  67. width: 100%;
  68. }
  69. .drawer-bg {
  70. background-color: rgba(0, 0, 0, 0.3);
  71. width: 100%;
  72. top: 0;
  73. height: 100%;
  74. position: absolute;
  75. z-index: 999;
  76. }
  77. .sidebar-container {
  78. background-color: var(--v3-sidebar-menu-bg-color);
  79. transition: width $transition-time;
  80. width: var(--v3-sidebar-width) !important;
  81. height: 100%;
  82. position: fixed;
  83. top: 0;
  84. bottom: 0;
  85. left: 0;
  86. z-index: 1001;
  87. overflow: hidden;
  88. border-right: var(--v3-sidebar-border-right);
  89. }
  90. .main-container {
  91. min-height: 100%;
  92. transition: margin-left $transition-time;
  93. margin-left: var(--v3-sidebar-width);
  94. position: relative;
  95. }
  96. .fixed-header {
  97. position: fixed !important;
  98. top: 0;
  99. right: 0;
  100. z-index: 9;
  101. width: calc(100% - var(--v3-sidebar-width));
  102. transition: width $transition-time;
  103. }
  104. .layout-header {
  105. position: relative;
  106. z-index: 9;
  107. background-color: var(--v3-header-bg-color);
  108. box-shadow: var(--v3-header-box-shadow);
  109. border-bottom: var(--v3-header-border-bottom);
  110. }
  111. .app-main {
  112. min-height: calc(100vh - var(--v3-navigationbar-height));
  113. position: relative;
  114. overflow: hidden;
  115. }
  116. .fixed-header + .app-main {
  117. padding-top: var(--v3-navigationbar-height);
  118. height: 100vh;
  119. overflow: auto;
  120. }
  121. .hasTagsView {
  122. .app-main {
  123. min-height: calc(100vh - var(--v3-header-height));
  124. }
  125. .fixed-header + .app-main {
  126. padding-top: var(--v3-header-height);
  127. }
  128. }
  129. .hideSidebar {
  130. .sidebar-container {
  131. width: var(--v3-sidebar-hide-width) !important;
  132. }
  133. .main-container {
  134. margin-left: var(--v3-sidebar-hide-width);
  135. }
  136. .fixed-header {
  137. width: calc(100% - var(--v3-sidebar-hide-width));
  138. }
  139. }
  140. // 适配 mobile 端
  141. .mobile {
  142. .sidebar-container {
  143. transition: transform $transition-time;
  144. width: var(--v3-sidebar-width) !important;
  145. }
  146. .main-container {
  147. margin-left: 0px;
  148. }
  149. .fixed-header {
  150. width: 100%;
  151. }
  152. &.openSidebar {
  153. position: fixed;
  154. top: 0;
  155. }
  156. &.hideSidebar {
  157. .sidebar-container {
  158. pointer-events: none;
  159. transition-duration: 0.3s;
  160. transform: translate3d(calc(0px - var(--v3-sidebar-width)), 0, 0);
  161. }
  162. }
  163. }
  164. .withoutAnimation {
  165. .sidebar-container,
  166. .main-container {
  167. transition: none;
  168. }
  169. }
  170. </style>