index.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <template>
  2. <el-scrollbar ref="scrollbarRef" wrap-class="scroll-wrap">
  3. <div class="vue-shop-vite-box" :class="{ mobile }">
  4. <component :is="layout" :collapse="collapse" :device="device" :fixed-header="theme.fixedHeader" :show-tabs="theme.showTabs" />
  5. </div>
  6. <vab-theme-drawer />
  7. <vab-theme-setting />
  8. <vab-statistics />
  9. <el-backtop target="#app .scroll-wrap" />
  10. </el-scrollbar>
  11. </template>
  12. <script lang="ts" setup>
  13. import { ElScrollbar } from 'element-plus'
  14. import { useSettingsStore } from '/@/store/modules/settings'
  15. import { useUserStore } from '/@/store/modules/user'
  16. import { convertToCamelCase } from '/@/utils/convertToCamelCase'
  17. defineOptions({
  18. name: 'Layout',
  19. })
  20. interface ComponentType {
  21. default: Component
  22. }
  23. const route = useRoute()
  24. const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
  25. const userStore = useUserStore()
  26. const { username } = storeToRefs(userStore)
  27. const settingsStore = useSettingsStore()
  28. const { device, collapse, theme } = storeToRefs(settingsStore)
  29. const { toggleDevice, foldSideBar, openSideBar, updateTheme } = settingsStore
  30. const mobile = ref(false)
  31. let oldLayout = theme.value.layout
  32. const visibility = useDocumentVisibility()
  33. const imports = import.meta.glob<ComponentType>('./**/*.vue', { eager: true })
  34. const Components: Record<string, Component> = {}
  35. Object.getOwnPropertyNames(imports).forEach((key: any) => {
  36. Components[key.replaceAll(/(\/|\.|index.vue)/g, '')] = imports[key].default
  37. })
  38. const layout = computed(() => Components[convertToCamelCase(`vab-layout-${theme.value.layout}`)])
  39. const resizeBody = () => {
  40. const { width } = useWindowSize()
  41. mobile.value = width.value - 1 < 992
  42. }
  43. watch(mobile, (value) => {
  44. if (value) {
  45. oldLayout = theme.value.layout
  46. foldSideBar()
  47. } else openSideBar()
  48. theme.value.layout = value ? 'vertical' : oldLayout
  49. toggleDevice(value ? 'mobile' : 'desktop')
  50. })
  51. onBeforeMount(() => {
  52. resizeBody()
  53. window.addEventListener('resize', resizeBody)
  54. updateTheme()
  55. })
  56. onBeforeUnmount(() => {
  57. if (mobile.value) theme.value.layout = oldLayout
  58. window.removeEventListener('resize', resizeBody)
  59. })
  60. watch(visibility, (current, previous) => {
  61. if (current === 'visible' && previous === 'hidden') $baseNotify(`尊敬的${username.value},欢迎回来`, '', 'success', 'bottom-right')
  62. })
  63. watch(
  64. route,
  65. () => {
  66. nextTick(() => {
  67. scrollbarRef.value!.setScrollTop(0)
  68. })
  69. },
  70. { immediate: true }
  71. )
  72. </script>
  73. <style lang="scss" scoped>
  74. .el-scrollbar__view.scroll-view {
  75. overflow: auto;
  76. }
  77. .vue-shop-vite-box {
  78. position: relative;
  79. width: 100%;
  80. height: 100%;
  81. [class*='vab-layout-'] {
  82. :deep() {
  83. .vab-layout-header {
  84. border-bottom: 1px solid var(--el-border-color);
  85. &.is-no-tabs {
  86. border-bottom: 0;
  87. }
  88. }
  89. }
  90. &.fixed {
  91. padding-top: calc(var(--el-nav-height) + var(--el-tabs-height));
  92. }
  93. &.fixed.no-tabs-bar {
  94. padding-top: var(--el-nav-height);
  95. }
  96. }
  97. :deep() {
  98. .fixed-header {
  99. position: fixed;
  100. top: 0;
  101. right: 0;
  102. z-index: calc(var(--el-z-index) - 1);
  103. width: 100%;
  104. }
  105. .vab-main {
  106. position: relative;
  107. width: auto;
  108. min-height: 100%;
  109. margin-left: var(--el-left-menu-width);
  110. &.is-collapse-main {
  111. margin-left: var(--el-left-menu-width-min);
  112. .fixed-header {
  113. width: var(--el-right-content-width-min);
  114. }
  115. }
  116. &:not(.is-collapse-main) {
  117. .fixed-header {
  118. width: calc(100% - var(--el-left-menu-width));
  119. }
  120. }
  121. }
  122. }
  123. /* 手机端开始 */
  124. &.mobile {
  125. :deep() {
  126. .vab-layout-vertical {
  127. .el-scrollbar.vab-side-bar {
  128. z-index: calc(var(--el-z-index) + 1);
  129. &.is-collapse {
  130. width: 0;
  131. }
  132. }
  133. .vab-main {
  134. .fixed-header {
  135. width: 100%;
  136. }
  137. margin-left: 0;
  138. }
  139. }
  140. /* 隐藏分页和页码跳转 */
  141. .el-pager,
  142. .el-pagination__jump {
  143. display: none;
  144. }
  145. }
  146. }
  147. /* 手机端结束 */
  148. }
  149. </style>