layout.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <script lang="ts" setup>
  2. import { computed } from 'vue';
  3. import { VbenAdminLayout } from '@vben-core/layout-ui';
  4. import { $t } from '@vben-core/locales';
  5. import {
  6. preferences,
  7. updatePreferences,
  8. usePreferences,
  9. } from '@vben-core/preferences';
  10. import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui';
  11. import { mapTree } from '@vben-core/toolkit';
  12. import { MenuRecordRaw } from '@vben-core/typings';
  13. import { Breadcrumb, CozeAssistant, Preferences } from '../widgets';
  14. import { LayoutContent } from './content';
  15. import { Copyright } from './copyright';
  16. import { LayoutFooter } from './footer';
  17. import { LayoutHeader } from './header';
  18. import {
  19. LayoutExtraMenu,
  20. LayoutMenu,
  21. LayoutMixedMenu,
  22. useExtraMenu,
  23. useMixedMenu,
  24. } from './menu';
  25. import { LayoutTabbar, LayoutTabbarTools } from './tabbar';
  26. defineOptions({ name: 'BasicLayout' });
  27. const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
  28. const { isDark, isHeaderNav, isMixedNav, isSideMixedNav, layout } =
  29. usePreferences();
  30. const headerMenuTheme = computed(() => {
  31. return isDark.value ? 'dark' : 'light';
  32. });
  33. const theme = computed(() => {
  34. const dark = isDark.value || preferences.app.semiDarkMenu;
  35. return dark ? 'dark' : 'light';
  36. });
  37. const logoClass = computed(() => {
  38. let cls = '';
  39. const { collapsed, collapsedShowTitle } = preferences.sidebar;
  40. if (collapsedShowTitle && collapsed && !isMixedNav.value) {
  41. cls += ' mx-auto';
  42. }
  43. if (isSideMixedNav.value) {
  44. cls += ' flex-center';
  45. }
  46. return cls;
  47. });
  48. const isMenuRounded = computed(() => {
  49. return preferences.navigation.styleType === 'rounded';
  50. });
  51. const logoCollapse = computed(() => {
  52. if (isHeaderNav.value || isMixedNav.value) {
  53. return false;
  54. }
  55. const { isMobile } = preferences.app;
  56. const { collapsed } = preferences.sidebar;
  57. if (!collapsed && isMobile) {
  58. return false;
  59. }
  60. return collapsed || isSideMixedNav.value;
  61. });
  62. const showHeaderNav = computed(() => {
  63. return isHeaderNav.value || isMixedNav.value;
  64. });
  65. const {
  66. extraActiveMenu,
  67. extraMenus,
  68. handleDefaultSelect,
  69. handleMenuMouseEnter,
  70. handleMixedMenuSelect,
  71. handleSideMouseLeave,
  72. sidebarExtraVisible,
  73. } = useExtraMenu();
  74. const {
  75. handleMenuSelect,
  76. headerActive,
  77. headerMenus,
  78. sideActive,
  79. sideMenus,
  80. sideVisible,
  81. } = useMixedMenu();
  82. function wrapperMenus(menus: MenuRecordRaw[]) {
  83. return mapTree(menus, (item) => {
  84. return { ...item, name: $t(item.name) };
  85. });
  86. }
  87. function toggleSidebar() {
  88. updatePreferences({
  89. sidebar: {
  90. hidden: !preferences.sidebar.hidden,
  91. },
  92. });
  93. }
  94. function clearPreferencesAndLogout() {
  95. emit('clearPreferencesAndLogout');
  96. }
  97. </script>
  98. <template>
  99. <VbenAdminLayout
  100. v-model:sidebar-extra-visible="sidebarExtraVisible"
  101. :content-compact="preferences.app.contentCompact"
  102. :footer-enable="preferences.footer.enable"
  103. :footer-fixed="preferences.footer.fixed"
  104. :header-hidden="preferences.header.hidden"
  105. :header-mode="preferences.header.mode"
  106. :header-visible="preferences.header.enable"
  107. :is-mobile="preferences.app.isMobile"
  108. :layout="layout"
  109. :sidebar-collapse="preferences.sidebar.collapsed"
  110. :sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
  111. :sidebar-enable="sideVisible"
  112. :sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
  113. :sidebar-extra-collapse="preferences.sidebar.extraCollapse"
  114. :sidebar-hidden="preferences.sidebar.hidden"
  115. :sidebar-semi-dark="preferences.app.semiDarkMenu"
  116. :sidebar-theme="theme"
  117. :sidebar-width="preferences.sidebar.width"
  118. :tabbar-enable="preferences.tabbar.enable"
  119. @side-mouse-leave="handleSideMouseLeave"
  120. @toggle-sidebar="toggleSidebar"
  121. @update:sidebar-collapse="
  122. (value: boolean) => updatePreferences({ sidebar: { collapsed: value } })
  123. "
  124. @update:sidebar-enable="
  125. (value: boolean) => updatePreferences({ sidebar: { enable: value } })
  126. "
  127. @update:sidebar-expand-on-hover="
  128. (value: boolean) =>
  129. updatePreferences({ sidebar: { expandOnHover: value } })
  130. "
  131. @update:sidebar-extra-collapse="
  132. (value: boolean) =>
  133. updatePreferences({ sidebar: { extraCollapse: value } })
  134. "
  135. >
  136. <template v-if="preferences.app.enablePreferences" #preferences>
  137. <Preferences @clear-preferences-and-logout="clearPreferencesAndLogout" />
  138. </template>
  139. <template #floating-groups>
  140. <CozeAssistant
  141. v-if="preferences.app.aiAssistant"
  142. :is-mobile="preferences.app.isMobile"
  143. />
  144. <VbenBackTop />
  145. </template>
  146. <!-- logo -->
  147. <template #logo>
  148. <VbenLogo
  149. :alt="preferences.app.name"
  150. :class="logoClass"
  151. :collapse="logoCollapse"
  152. :src="preferences.logo.source"
  153. :text="preferences.app.name"
  154. :theme="showHeaderNav ? headerMenuTheme : theme"
  155. />
  156. </template>
  157. <!-- 头部区域 -->
  158. <template #header>
  159. <LayoutHeader :theme="theme">
  160. <template
  161. v-if="!showHeaderNav && preferences.breadcrumb.enable"
  162. #breadcrumb
  163. >
  164. <Breadcrumb
  165. :hide-when-only-one="preferences.breadcrumb.hideOnlyOne"
  166. :show-home="preferences.breadcrumb.showHome"
  167. :show-icon="preferences.breadcrumb.showIcon"
  168. :type="preferences.breadcrumb.styleType"
  169. />
  170. </template>
  171. <template v-if="showHeaderNav" #menu>
  172. <LayoutMenu
  173. :default-active="headerActive"
  174. :menus="wrapperMenus(headerMenus)"
  175. :rounded="isMenuRounded"
  176. :theme="headerMenuTheme"
  177. class="w-full"
  178. mode="horizontal"
  179. @select="handleMenuSelect"
  180. />
  181. </template>
  182. <template #user-dropdown>
  183. <slot name="user-dropdown"></slot>
  184. </template>
  185. <template #notification>
  186. <slot name="notification"></slot>
  187. </template>
  188. </LayoutHeader>
  189. </template>
  190. <!-- 侧边菜单区域 -->
  191. <template #menu>
  192. <LayoutMenu
  193. :accordion="preferences.navigation.accordion"
  194. :collapse="preferences.sidebar.collapsed"
  195. :collapse-show-title="preferences.sidebar.collapsedShowTitle"
  196. :default-active="sideActive"
  197. :menus="wrapperMenus(sideMenus)"
  198. :rounded="isMenuRounded"
  199. :theme="theme"
  200. mode="vertical"
  201. @select="handleMenuSelect"
  202. />
  203. </template>
  204. <template #mixed-menu>
  205. <LayoutMixedMenu
  206. :active-path="extraActiveMenu"
  207. :collapse="!preferences.sidebar.collapsedShowTitle"
  208. :menus="wrapperMenus(headerMenus)"
  209. :rounded="isMenuRounded"
  210. :theme="theme"
  211. @default-select="handleDefaultSelect"
  212. @enter="handleMenuMouseEnter"
  213. @select="handleMixedMenuSelect"
  214. />
  215. </template>
  216. <!-- 侧边额外区域 -->
  217. <template #side-extra>
  218. <LayoutExtraMenu
  219. :accordion="preferences.navigation.accordion"
  220. :collapse="preferences.sidebar.extraCollapse"
  221. :menus="wrapperMenus(extraMenus)"
  222. :rounded="isMenuRounded"
  223. :theme="theme"
  224. />
  225. </template>
  226. <template #side-extra-title>
  227. <VbenLogo
  228. v-if="preferences.logo.enable"
  229. :alt="preferences.app.name"
  230. :text="preferences.app.name"
  231. :theme="theme"
  232. />
  233. </template>
  234. <template #tabbar>
  235. <LayoutTabbar
  236. v-if="preferences.tabbar.enable"
  237. :show-icon="preferences.tabbar.showIcon"
  238. />
  239. </template>
  240. <template #tabbar-tools>
  241. <LayoutTabbarTools v-if="preferences.tabbar.enable" />
  242. </template>
  243. <!-- 主体内容 -->
  244. <template #content>
  245. <LayoutContent />
  246. </template>
  247. <!-- 页脚 -->
  248. <template v-if="preferences.footer.enable" #footer>
  249. <LayoutFooter>
  250. <Copyright
  251. v-if="preferences.copyright.enable"
  252. v-bind="preferences.copyright"
  253. />
  254. </LayoutFooter>
  255. </template>
  256. </VbenAdminLayout>
  257. </template>