index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <template>
  2. <div
  3. class="admin-layout"
  4. :inverted="inverted"
  5. :class="{
  6. 'admin-layout-fix-header': fixedHeader,
  7. 'admin-layout-fix-side': fixedSide,
  8. 'admin-layout-fix-body': true,
  9. 'admin-layout-side-horizontal': navMode === 'horizontal',
  10. 'admin-layout-hide-side': !isMixMenuNoneSub,
  11. 'admin-layout-show-tabs': isMultiTabs,
  12. 'admin-layout-collapse': collapsed,
  13. 'admin-layout-theme-light': getNavTheme === 'light',
  14. 'admin-layout-header-dark': getNavTheme === 'header-dark',
  15. }"
  16. >
  17. <div class="admin-layout-header">
  18. <Logo v-if="navMode != 'horizontal'" />
  19. <PageHeader @update:collapsed="updateCollapsed" :inverted="inverted" />
  20. </div>
  21. <div class="admin-layout-content">
  22. <div
  23. v-if="isMixMenuNoneSub && (navMode === 'vertical' || navMode === 'horizontal-mix')"
  24. show-trigger="arrow-circle"
  25. @collapse="collapsed = true"
  26. @expand="collapsed = false"
  27. :native-scrollbar="false"
  28. :collapsed="collapsed"
  29. collapse-mode="width"
  30. :collapsed-width="64"
  31. :width="leftMenuWidth"
  32. :inverted="inverted"
  33. class="admin-layout-sider"
  34. >
  35. <el-scrollbar>
  36. <Sider v-model:location="getMenuLocation" class="left-menu" v-bind="siderOption" />
  37. </el-scrollbar>
  38. </div>
  39. <div
  40. embedded
  41. :inverted="inverted"
  42. class="admin-layout-content-son"
  43. :class="{
  44. 'layout-content-inverted': getDarkTheme,
  45. 'page-full-screen': isFullscreen && !getDarkTheme,
  46. }"
  47. >
  48. <TabsView
  49. v-if="isMultiTabs"
  50. v-model:collapsed="collapsed"
  51. @page-full-screen="togglePageFullScreen"
  52. />
  53. <div class="admin-layout-content-main">
  54. <div class="main-view" ref="adminBodyRef">
  55. <MainView />
  56. </div>
  57. </div>
  58. <!-- <el-back-top :right="100" /> -->
  59. </div>
  60. </div>
  61. <div class="admin-layout-shade"></div>
  62. <InvalidAuth />
  63. </div>
  64. <!--项目配置-->
  65. <template v-if="getIsProjectSetting">
  66. <ProjectSetting ref="drawerSetting" />
  67. <div class="shadow-lg circular" @click="openSetting">
  68. <el-icon class="el-input__icon" :size="20">
  69. <SettingOutlined class="transition ease-in-out delay-150 transform hover:animate-spin" />
  70. </el-icon>
  71. </div>
  72. </template>
  73. </template>
  74. <script lang="ts" setup>
  75. import { ref, unref, computed, onMounted, watch, provide } from 'vue';
  76. import { Logo } from './components/Logo';
  77. import { TabsView } from './components/TagsView';
  78. import { MainView } from './components/Main';
  79. import { PageHeader } from './components/Header';
  80. import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
  81. import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
  82. import { useRoute } from 'vue-router';
  83. import { useProjectSettingStore } from '@/store/modules/projectSetting';
  84. import ProjectSetting from './components/Header/ProjectSetting.vue';
  85. import { useFullscreen } from '@vueuse/core';
  86. import { useDesignSettingStore } from '@/store/modules/designSetting';
  87. import { SettingOutlined } from '@vicons/antd';
  88. import Sider from './components/Sider/Sider.vue';
  89. import InvalidAuth from '@/components/InvalidAuth/InvalidAuth.vue';
  90. const { getDarkTheme } = useDesignSetting();
  91. const {
  92. getNavMode,
  93. getMenuWidth,
  94. getMenuMinWidth,
  95. getNavTheme,
  96. getHeaderSetting,
  97. getMenuSetting,
  98. getMultiTabsSetting,
  99. getIsProjectSetting,
  100. } = useProjectSetting();
  101. const settingStore = useProjectSettingStore();
  102. const designStore = useDesignSettingStore();
  103. const navMode = getNavMode;
  104. const drawerSetting = ref();
  105. const collapsed = ref<boolean>(false);
  106. const adminBodyRef = ref<HTMLElement | null>(null);
  107. const { isFullscreen, toggle } = useFullscreen(adminBodyRef);
  108. provide('isPageFullScreen', isFullscreen);
  109. provide('collapsed', collapsed);
  110. provide('openSetting', openSetting);
  111. watch(
  112. () => collapsed.value,
  113. (to) => {
  114. settingStore.setMenuSetting({ collapsed: to });
  115. },
  116. { immediate: true },
  117. );
  118. //固定顶部
  119. const fixedHeader = computed(() => {
  120. const { fixed } = unref(getHeaderSetting);
  121. return fixed;
  122. });
  123. //固定侧边栏
  124. const fixedSide = computed(() => {
  125. const { fixed } = unref(getMenuSetting);
  126. return fixed;
  127. });
  128. //切换内容页全屏
  129. function togglePageFullScreen() {
  130. toggle();
  131. }
  132. //菜单折叠
  133. function updateCollapsed() {
  134. collapsed.value = !collapsed.value;
  135. }
  136. //打开设置
  137. function openSetting() {
  138. const { openDrawer } = drawerSetting.value;
  139. openDrawer();
  140. }
  141. //获取主题风格色
  142. const getAppTheme = computed(() => {
  143. return designStore.appTheme;
  144. });
  145. const isMixMenuNoneSub = computed(() => {
  146. const mixMenu = settingStore.menuSetting.mixMenu;
  147. const currentRoute = useRoute();
  148. if (unref(navMode) != 'horizontal-mix') return true;
  149. if (unref(navMode) === 'horizontal-mix' && mixMenu && currentRoute.meta.isRoot) {
  150. return false;
  151. }
  152. return true;
  153. });
  154. const isMultiTabs = computed(() => {
  155. return unref(getMultiTabsSetting).show;
  156. });
  157. const inverted = computed(() => {
  158. return ['dark', 'header-dark'].includes(unref(getNavTheme));
  159. });
  160. const siderOption = computed(() => {
  161. const navTheme = unref(getNavTheme);
  162. let backgroundColor = '#001428';
  163. let textColor = '#bbb';
  164. if (unref(getDarkTheme)) {
  165. backgroundColor = '#18181c';
  166. textColor = '#fff';
  167. } else if (['light'].includes(navTheme)) {
  168. backgroundColor = '#fff';
  169. textColor = '#333';
  170. }
  171. return {
  172. backgroundColor,
  173. textColor,
  174. };
  175. });
  176. const leftMenuWidth = computed(() => {
  177. const { minMenuWidth, menuWidth } = unref(getMenuSetting);
  178. return collapsed.value ? minMenuWidth : menuWidth;
  179. });
  180. const getMenuLocation = computed(() => {
  181. return 'left';
  182. });
  183. const menuWidth = computed(() => {
  184. return getMenuWidth.value + 'px';
  185. });
  186. const minMenuWidth = computed(() => {
  187. return getMenuMinWidth.value + 'px';
  188. });
  189. //看自身需求是否保留吧,这个用处不是很大
  190. const watchWidth = () => {
  191. const { isFullscreen: isFullscreen } = useFullscreen();
  192. if (isFullscreen.value) return;
  193. const Width = document.body.clientWidth;
  194. if (Width < 750) {
  195. collapsed.value = true;
  196. }
  197. };
  198. onMounted(() => {
  199. window.addEventListener('resize', watchWidth);
  200. });
  201. </script>
  202. <style lang="scss" scoped>
  203. .admin-layout {
  204. color: rgb(51, 54, 57);
  205. background-color: #fff;
  206. box-sizing: border-box;
  207. position: relative;
  208. z-index: auto;
  209. transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1),
  210. background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  211. &-shade {
  212. position: fixed;
  213. top: 0;
  214. left: 0;
  215. right: 0;
  216. bottom: 0;
  217. z-index: 101;
  218. transition: background-color 0.3s cubic-bezier(0.2, 0, 0, 1) 0s,
  219. left 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
  220. visibility: hidden;
  221. }
  222. //侧边栏
  223. &-sider {
  224. min-height: calc(100vh - 64px);
  225. box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
  226. position: relative;
  227. z-index: 13;
  228. transition: width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s, left 0.3s cubic-bezier(0.2, 0, 0, 1) 0s,
  229. box-shadow 0.3s cubic-bezier(0.2, 0, 0, 1) 0s, border-color var(--el-transition-duration),
  230. background-color var(--el-transition-duration), color var(--el-transition-duration);
  231. background-color: #001428;
  232. width: v-bind(menuWidth);
  233. :deep(.el-menu) {
  234. border-right: none;
  235. }
  236. }
  237. //主体内容区域
  238. &-content {
  239. width: 100%;
  240. :deep(.n-layout-scroll-container) {
  241. overflow: hidden;
  242. }
  243. &-main {
  244. background: #f5f7f9;
  245. padding: 10px;
  246. overflow-x: hidden;
  247. }
  248. &-son {
  249. flex: 1;
  250. transition: padding-left 0.3s cubic-bezier(0.2, 0, 0, 1) 0s,
  251. box-shadow 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
  252. }
  253. }
  254. //深色主题
  255. .layout-content-inverted {
  256. background: rgb(16, 16, 20);
  257. }
  258. .n-layout-header.n-layout-header--absolute-positioned {
  259. z-index: 11;
  260. }
  261. .n-layout-footer {
  262. background: none;
  263. }
  264. // 固定顶部
  265. &-fix-header {
  266. position: fixed;
  267. top: 0;
  268. left: 0;
  269. right: 0;
  270. .admin-layout-content {
  271. flex: auto;
  272. min-height: calc(100vh - 64px);
  273. &-son {
  274. :deep(.n-layout-scroll-container) {
  275. overflow: hidden;
  276. }
  277. }
  278. &-main {
  279. padding: 12px 12px 0 12px;
  280. height: calc(100vh - 64px);
  281. position: relative;
  282. overflow-y: auto;
  283. }
  284. }
  285. }
  286. //固定侧栏
  287. &-fix-side {
  288. .admin-layout-sider {
  289. position: fixed;
  290. left: 0;
  291. bottom: 0;
  292. top: 64px;
  293. }
  294. .admin-layout-content-son {
  295. padding-left: v-bind(menuWidth);
  296. }
  297. .admin-layout-header {
  298. .logo {
  299. position: fixed;
  300. left: 0;
  301. top: 0;
  302. z-index: 15;
  303. }
  304. :deep(.layout-header) {
  305. padding-left: v-bind(menuWidth);
  306. }
  307. }
  308. }
  309. //折叠
  310. &-collapse {
  311. .admin-layout-content-son {
  312. padding-left: v-bind(minMenuWidth);
  313. }
  314. .admin-layout-header {
  315. :deep(.layout-header) {
  316. padding-left: v-bind(minMenuWidth);
  317. }
  318. }
  319. .admin-layout-sider {
  320. width: v-bind(minMenuWidth);
  321. }
  322. }
  323. //没有左侧菜单
  324. &-hide-side {
  325. .admin-layout-content-son {
  326. padding-left: 0px;
  327. }
  328. }
  329. //显示多标签
  330. &-show-tabs {
  331. .admin-layout-content {
  332. &-main {
  333. padding: 0 10px 10px 10px;
  334. }
  335. }
  336. }
  337. &-fix-header.admin-layout-show-tabs {
  338. .admin-layout-content {
  339. &-main {
  340. height: calc(100vh - 64px - 44px);
  341. //padding: 0 10px 10px 10px;
  342. }
  343. }
  344. }
  345. //横向菜单
  346. &-side-horizontal {
  347. //处理顶部菜单
  348. .admin-layout-header {
  349. .logo {
  350. position: fixed;
  351. left: 0;
  352. top: 0;
  353. z-index: 15;
  354. }
  355. :deep(.layout-header) {
  356. padding-left: 0px;
  357. }
  358. }
  359. //处理内容区域
  360. .admin-layout-content-son {
  361. padding-left: 0px;
  362. }
  363. }
  364. &-theme-light {
  365. .admin-layout-content .admin-layout-sider {
  366. background-color: #fff;
  367. }
  368. .el-menu {
  369. --el-menu-bg-color: #fff;
  370. --el-menu-text-color: rgb(51, 54, 57);
  371. --el-menu-hover-bg-color: #2d8cf0;
  372. --el-menu-hover-text-color: #fff;
  373. }
  374. }
  375. //暗色顶栏
  376. &-header-dark {
  377. .admin-layout-header {
  378. :deep(.layout-header) {
  379. background-color: #001428;
  380. color: #fff;
  381. .link-text {
  382. color: #fff;
  383. }
  384. }
  385. }
  386. }
  387. }
  388. //内容全屏
  389. .page-full-screen {
  390. .main-view {
  391. background: #f0f2f5;
  392. }
  393. }
  394. .dark {
  395. .page-full-screen {
  396. .main-view {
  397. background: #000;
  398. }
  399. }
  400. }
  401. .circular {
  402. position: fixed;
  403. right: -2px;
  404. top: 50%;
  405. transform: translateY(-50%);
  406. display: flex;
  407. align-items: center;
  408. justify-content: center;
  409. width: 40px;
  410. height: 40px;
  411. background-color: v-bind(getAppTheme);
  412. font-size: 24px;
  413. color: #fff;
  414. border-radius: 10px 0 0 10px;
  415. cursor: pointer;
  416. z-index: 200;
  417. }
  418. </style>