index.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <a-layout class="vab-layout-wrap">
  3. <div
  4. v-if="device === 'mobile' && !collapse"
  5. class="vab-mask"
  6. @click="handleFoldSideBar"
  7. ></div>
  8. <a-layout-sider
  9. collapsible
  10. class="vab-sider"
  11. width="220"
  12. v-model:collapsed="collapse"
  13. :class="classObj"
  14. :trigger="null"
  15. >
  16. <vab-logo />
  17. <a-menu
  18. class="vab-menu"
  19. theme="dark"
  20. mode="inline"
  21. v-model:selectedKeys="selectedKeys"
  22. v-model:openKeys="openKeys"
  23. >
  24. <vab-menu v-for="route in routes" :key="route.path" :item="route" />
  25. </a-menu>
  26. </a-layout-sider>
  27. <a-layout
  28. class="vab-layout"
  29. :class="'mobile' === device ? 'vab-mobile-layout' : ''"
  30. >
  31. <a-layout-header class="vab-header">
  32. <a-row>
  33. <a-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
  34. <menu-unfold-outlined
  35. v-if="collapse"
  36. class="trigger"
  37. @click="toggleCollapse"
  38. />
  39. <menu-fold-outlined
  40. v-else
  41. class="trigger"
  42. @click="toggleCollapse"
  43. />
  44. </a-col>
  45. <a-col :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
  46. <vab-avatar />
  47. </a-col>
  48. </a-row>
  49. </a-layout-header>
  50. <vab-tabs />
  51. <vab-content />
  52. </a-layout>
  53. </a-layout>
  54. </template>
  55. <script>
  56. import VabLogo from './vab-logo'
  57. import VabAvatar from './vab-avatar'
  58. import VabMenu from './vab-menu'
  59. import VabTabs from './vab-tabs'
  60. import VabContent from './vab-content'
  61. import { mapActions, mapGetters } from 'vuex'
  62. import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'
  63. export default {
  64. components: {
  65. VabLogo,
  66. VabAvatar,
  67. VabMenu,
  68. VabTabs,
  69. VabContent,
  70. MenuUnfoldOutlined,
  71. MenuFoldOutlined,
  72. },
  73. data() {
  74. return {
  75. selectedKeys: [],
  76. openKeys: [],
  77. }
  78. },
  79. computed: {
  80. ...mapGetters({
  81. collapse: 'settings/collapse',
  82. routes: 'routes/routes',
  83. device: 'settings/device',
  84. }),
  85. classObj() {
  86. return {
  87. 'vab-mobile': this.device === 'mobile',
  88. 'vab-collapse': this.collapse,
  89. }
  90. },
  91. },
  92. watch: {
  93. $route: {
  94. handler({ path, matched }) {
  95. matched[0].children.length > 1
  96. ? (this.selectedKeys = [path])
  97. : (this.selectedKeys = [matched[0].path])
  98. this.openKeys = [matched[0].path]
  99. },
  100. immediate: true,
  101. },
  102. },
  103. beforeMount() {
  104. window.addEventListener('resize', this.handleLayouts)
  105. },
  106. beforeUnmount() {
  107. window.removeEventListener('resize', this.handleLayouts)
  108. },
  109. mounted() {
  110. this.handleLayouts()
  111. },
  112. methods: {
  113. ...mapActions({
  114. toggleDevice: 'settings/toggleDevice',
  115. handleFoldSideBar: 'settings/foldSideBar',
  116. toggleCollapse: 'settings/toggleCollapse',
  117. }),
  118. handleLayouts() {
  119. const width = document.body.getBoundingClientRect().width
  120. if (this.width !== width) {
  121. const isMobile = width - 1 < 992
  122. this.toggleDevice(isMobile ? 'mobile' : 'desktop')
  123. this.width = width
  124. }
  125. },
  126. },
  127. }
  128. </script>
  129. <style lang="scss">
  130. .vab-layout-wrap {
  131. height: 100%;
  132. .vab-sider {
  133. background-color: #fff;
  134. position: fixed;
  135. left: 0;
  136. height: 100vh;
  137. overflow: auto;
  138. .vab-menu {
  139. overflow-y: auto;
  140. height: calc(100vh - $vab-header-height);
  141. }
  142. .vab-menu::-webkit-scrollbar {
  143. display: none;
  144. }
  145. }
  146. .vab-layout {
  147. height: 100%;
  148. overflow: auto;
  149. padding-left: 220px;
  150. transition: all 0.2s;
  151. background-color: #f0f3f4;
  152. }
  153. .vab-mobile-layout {
  154. padding-left: 0;
  155. transition: all 0.2s;
  156. }
  157. .vab-collapse {
  158. .vab-logo .anticon + span {
  159. display: inline-block;
  160. max-width: 0;
  161. opacity: 0;
  162. transition: all 0.2s;
  163. }
  164. & + .vab-layout {
  165. padding-left: 81px;
  166. transition: all 0.2s;
  167. }
  168. }
  169. .vab-mask {
  170. position: fixed;
  171. top: 0;
  172. right: 0;
  173. bottom: 0;
  174. left: 0;
  175. z-index: 998;
  176. width: 100%;
  177. height: 100vh;
  178. overflow: hidden;
  179. background: #000;
  180. opacity: 0.5;
  181. }
  182. .vab-mobile {
  183. position: fixed !important;
  184. z-index: 999;
  185. &.vab-collapse {
  186. width: 0 !important;
  187. min-width: 0 !important;
  188. max-width: 0 !important;
  189. * {
  190. display: none !important;
  191. width: 0 !important;
  192. min-width: 0 !important;
  193. max-width: 0 !important;
  194. }
  195. .ant-menu-item,
  196. .ant-menu-submenu {
  197. display: none !important;
  198. width: 0 !important;
  199. min-width: 0 !important;
  200. max-width: 0 !important;
  201. }
  202. & + .vab-layout {
  203. padding-left: 0px !important;
  204. transition: all 0.2s;
  205. }
  206. }
  207. }
  208. .vab-header {
  209. padding: 0;
  210. background: #fff;
  211. .ant-col + .ant-col {
  212. display: flex;
  213. justify-content: flex-end;
  214. padding: 0 $vab-padding;
  215. }
  216. .trigger {
  217. height: $vab-header-height;
  218. padding: 0 $vab-padding;
  219. font-size: 18px;
  220. line-height: $vab-header-height;
  221. cursor: pointer;
  222. transition: color 0.3s;
  223. &:hover {
  224. color: #1890ff;
  225. }
  226. }
  227. }
  228. }
  229. </style>