index.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. <script setup lang="ts">
  2. import { isEmpty } from 'lodash-es'
  3. import { computed, defineAsyncComponent, watch } from 'vue'
  4. import texturePng from '@/assets/texture.png'
  5. import { CollapseTransition, EmptyPlaceholder } from '@/components'
  6. import { useInjection } from '@/composables'
  7. import { mediaQueryInjectionKey, layoutInjectionKey } from '@/injection'
  8. import { usePreferencesStore, useTabsStore } from '@/stores'
  9. import AsideLayout from './aside/index.vue'
  10. import Tabs from './component/Tabs.vue'
  11. import FooterLayout from './footer/index.vue'
  12. import HeaderLayout from './header/index.vue'
  13. import MainLayout from './main/index.vue'
  14. defineOptions({
  15. name: 'Layout',
  16. })
  17. const MobileHeader = defineAsyncComponent(() => import('./mobile/MobileHeader.vue'))
  18. const MobileLeftAside = defineAsyncComponent(() => import('./mobile/MobileLeftAside.vue'))
  19. const MobileRightAside = defineAsyncComponent(() => import('./mobile/MobileRightAside.vue'))
  20. const tabsStore = useTabsStore()
  21. const preferencesStore = usePreferencesStore()
  22. const { isSmallScreen } = useInjection(mediaQueryInjectionKey)
  23. const { layoutSlideDirection, setLayoutSlideDirection } = useInjection(layoutInjectionKey)
  24. const layoutTranslateOffset = computed(() => {
  25. return layoutSlideDirection.value === 'right'
  26. ? preferencesStore.preferences.sidebarMenu.maxWidth || 0
  27. : layoutSlideDirection.value === 'left'
  28. ? -64
  29. : 0
  30. })
  31. watch(
  32. () => isSmallScreen.value,
  33. (isSmallScreen) => {
  34. if (isSmallScreen) {
  35. preferencesStore.modify({
  36. sidebarMenu: {
  37. collapsed: false,
  38. },
  39. })
  40. setLayoutSlideDirection(null)
  41. }
  42. },
  43. )
  44. </script>
  45. <template>
  46. <div
  47. class="relative flex h-svh overflow-hidden"
  48. :style="{ backgroundImage: `url(${texturePng})` }"
  49. >
  50. <MobileLeftAside v-if="isSmallScreen" />
  51. <div
  52. class="relative flex h-full w-full flex-col max-sm:bg-naive-card/50"
  53. :class="{
  54. 'border-naive-border transition-[background-color,border-color,rounded,transform]':
  55. isSmallScreen,
  56. 'rounded-xl border pb-2': isSmallScreen && layoutTranslateOffset,
  57. }"
  58. :style="
  59. isSmallScreen &&
  60. layoutSlideDirection && {
  61. transform: `translate(${layoutTranslateOffset}px) scale(0.88)`,
  62. }
  63. "
  64. >
  65. <HeaderLayout v-if="!isSmallScreen" />
  66. <MobileHeader v-else />
  67. <div class="flex flex-1 overflow-hidden">
  68. <CollapseTransition
  69. v-if="!isSmallScreen"
  70. :display="preferencesStore.preferences.navigationMode === 'sidebar'"
  71. content-class="min-h-0"
  72. >
  73. <AsideLayout />
  74. </CollapseTransition>
  75. <div class="flex flex-1 flex-col overflow-hidden">
  76. <CollapseTransition
  77. v-if="!isSmallScreen"
  78. :display="!isEmpty(tabsStore.tabs) && preferencesStore.preferences.showTabs"
  79. direction="horizontal"
  80. :render-content="false"
  81. >
  82. <Tabs />
  83. </CollapseTransition>
  84. <div class="relative flex-1 overflow-hidden">
  85. <MainLayout />
  86. </div>
  87. <EmptyPlaceholder
  88. :show="isEmpty(tabsStore.tabs)"
  89. description="空标签页"
  90. size="huge"
  91. >
  92. <template #icon>
  93. <div class="flex items-center justify-center">
  94. <span class="iconify ph--rectangle" />
  95. </div>
  96. </template>
  97. </EmptyPlaceholder>
  98. <CollapseTransition
  99. v-if="!isSmallScreen"
  100. :display="preferencesStore.preferences.showFooter"
  101. direction="horizontal"
  102. :render-content="false"
  103. >
  104. <FooterLayout />
  105. </CollapseTransition>
  106. </div>
  107. </div>
  108. <div
  109. v-if="isSmallScreen && layoutSlideDirection"
  110. class="absolute inset-0"
  111. @click="setLayoutSlideDirection(null)"
  112. />
  113. </div>
  114. <MobileRightAside v-if="isSmallScreen" />
  115. </div>
  116. </template>