DashboardLayout.vue 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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 class="layout-mask p-component-overlay" v-if="mobileMenuActive"></div>
  52. </Transition>
  53. </div>
  54. </template>
  55. <style lang="scss" scoped>
  56. .layout-sidebar {
  57. position: fixed;
  58. width: 300px;
  59. height: calc(100vh - 9rem);
  60. z-index: 999;
  61. overflow-y: auto;
  62. user-select: none;
  63. top: 7rem;
  64. left: 2rem;
  65. transition: transform 0.2s, left 0.2s;
  66. background-color: var(--surface-overlay);
  67. border-radius: 4px;
  68. padding: 1.5rem;
  69. box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.02), 0px 0px 2px rgba(0, 0, 0, 0.05), 0px 1px 4px rgba(0, 0, 0, 0.08);
  70. }
  71. .layout-main-container {
  72. display: flex;
  73. flex-direction: column;
  74. min-height: 100vh;
  75. justify-content: space-between;
  76. padding: 7rem 2rem 2rem 4rem;
  77. transition: margin-left 0.2s;
  78. }
  79. .layout-main {
  80. flex: 1 1 auto;
  81. }
  82. @media (min-width: 992px) {
  83. .layout-wrapper {
  84. .layout-main-container {
  85. margin-left: 300px;
  86. }
  87. &.layout-static-sidebar-inactive {
  88. .layout-sidebar {
  89. transform: translateX(-100%);
  90. left: 0;
  91. }
  92. .layout-main-container {
  93. margin-left: 0;
  94. padding-left: 2rem;
  95. }
  96. }
  97. .layout-mask {
  98. display: none;
  99. }
  100. }
  101. }
  102. @media (max-width: 991px) {
  103. .layout-wrapper {
  104. .layout-main-container {
  105. margin-left: 0;
  106. padding-left: 2rem;
  107. }
  108. .layout-sidebar {
  109. transform: translateX(-100%);
  110. left: 0;
  111. top: 0;
  112. height: 100vh;
  113. border-top-left-radius: 0;
  114. border-bottom-left-radius: 0;
  115. }
  116. .layout-mask {
  117. z-index: 998;
  118. background-color: var(--maskbg);
  119. &.layout-mask-enter-from,
  120. &.layout-mask-leave-to {
  121. background-color: transparent;
  122. }
  123. }
  124. &.layout-mobile-sidebar-active {
  125. .layout-sidebar {
  126. transform: translateX(0);
  127. }
  128. .layout-mask {
  129. display: block;
  130. }
  131. }
  132. }
  133. }
  134. </style>