DashboardLayout.vue 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <script setup>
  2. import { ref, computed } from 'vue'
  3. import AppTopBar from '@/components/AppTopBar.vue'
  4. import AppSidebar from '@/components/AppSidebar.vue'
  5. import AppFooter from '@/components/AppFooter.vue'
  6. import AppMessage from '@/components/AppMessage.vue'
  7. import menu from '@/utils/menu'
  8. const mobileMenuActive = ref(false)
  9. const staticMenuInactive = ref(false)
  10. const menuClick = ref(false)
  11. const isDesktop = () => window.innerWidth >= 992
  12. const containerClass = computed(() => {
  13. return [
  14. 'layout-wrapper',
  15. {
  16. 'layout-static-sidebar-inactive': staticMenuInactive.value,
  17. 'layout-mobile-sidebar-active': mobileMenuActive.value,
  18. },
  19. ]
  20. })
  21. const onWrapperClick = () => {
  22. if (!menuClick.value) {
  23. mobileMenuActive.value = false
  24. }
  25. menuClick.value = false
  26. }
  27. const onMenuToggle = (event) => {
  28. menuClick.value = true
  29. if (isDesktop()) {
  30. staticMenuInactive.value = !staticMenuInactive.value
  31. } else {
  32. mobileMenuActive.value = !mobileMenuActive.value
  33. }
  34. event.preventDefault()
  35. }
  36. </script>
  37. <template>
  38. <div :class="containerClass" @click="onWrapperClick">
  39. <AppTopBar @menu-toggle="onMenuToggle" />
  40. <div class="layout-sidebar">
  41. <AppSidebar :menu="menu[$page.props.auth.user.role_id]" />
  42. </div>
  43. <div class="layout-main-container">
  44. <div class="layout-main">
  45. <AppMessage />
  46. <slot />
  47. </div>
  48. <AppFooter />
  49. </div>
  50. <Transition name="layout-mask">
  51. <div
  52. class="layout-mask p-component-overlay"
  53. v-if="mobileMenuActive"
  54. ></div>
  55. </Transition>
  56. </div>
  57. </template>
  58. <style lang="scss" scoped>
  59. .layout-sidebar {
  60. position: fixed;
  61. width: 300px;
  62. height: calc(100vh - 9rem);
  63. z-index: 996;
  64. overflow-y: auto;
  65. user-select: none;
  66. top: 7rem;
  67. left: 2rem;
  68. transition: transform 0.2s, left 0.2s;
  69. background-color: var(--surface-overlay);
  70. border-radius: 4px;
  71. padding: 1.5rem;
  72. box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.02), 0px 0px 2px rgba(0, 0, 0, 0.05),
  73. 0px 1px 4px rgba(0, 0, 0, 0.08);
  74. }
  75. .layout-main-container {
  76. display: flex;
  77. flex-direction: column;
  78. min-height: 100vh;
  79. justify-content: space-between;
  80. padding: 7rem 2rem 2rem 4rem;
  81. transition: margin-left 0.2s;
  82. }
  83. .layout-main {
  84. flex: 1 1 auto;
  85. }
  86. @media (min-width: 992px) {
  87. .layout-wrapper {
  88. .layout-main-container {
  89. margin-left: 300px;
  90. }
  91. &.layout-static-sidebar-inactive {
  92. .layout-sidebar {
  93. transform: translateX(-100%);
  94. left: 0;
  95. }
  96. .layout-main-container {
  97. margin-left: 0;
  98. padding-left: 2rem;
  99. }
  100. }
  101. .layout-mask {
  102. display: none;
  103. }
  104. }
  105. }
  106. @media (max-width: 991px) {
  107. .layout-wrapper {
  108. .layout-main-container {
  109. margin-left: 0;
  110. padding-left: 2rem;
  111. }
  112. .layout-sidebar {
  113. transform: translateX(-100%);
  114. left: 0;
  115. top: 0;
  116. height: 100vh;
  117. border-top-left-radius: 0;
  118. border-bottom-left-radius: 0;
  119. }
  120. .layout-mask {
  121. z-index: 995;
  122. background-color: var(--maskbg);
  123. &.layout-mask-enter-from,
  124. &.layout-mask-leave-to {
  125. background-color: transparent;
  126. }
  127. }
  128. &.layout-mobile-sidebar-active {
  129. .layout-sidebar {
  130. transform: translateX(0);
  131. }
  132. .layout-mask {
  133. display: block;
  134. }
  135. }
  136. }
  137. }
  138. </style>