MixSider.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <template>
  2. <div :class="`${prefixCls}-dom`" />
  3. <div
  4. v-click-outside="handleClickOutside"
  5. :class="[
  6. prefixCls,
  7. getMenuTheme,
  8. {
  9. open: openMenu,
  10. },
  11. ]"
  12. >
  13. <AppLogo :showTitle="false" :class="`${prefixCls}-logo`" />
  14. <ScrollContainer>
  15. <ul :class="`${prefixCls}-module`">
  16. <li
  17. :class="[
  18. `${prefixCls}-module__item `,
  19. {
  20. [`${prefixCls}-module__item--active`]: item.path === activePath,
  21. },
  22. ]"
  23. v-for="item in menuModules"
  24. :key="item.path"
  25. @click="hanldeModuleClick(item.path)"
  26. >
  27. <MenuTag :item="item" :showTitle="false" :isHorizontal="false" />
  28. <g-icon
  29. :class="`${prefixCls}-module__icon`"
  30. :size="22"
  31. :icon="item.meta && item.meta.icon"
  32. />
  33. <p :class="`${prefixCls}-module__name`">{{ t(item.name) }}</p>
  34. </li>
  35. </ul>
  36. </ScrollContainer>
  37. <div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
  38. <div
  39. v-show="openMenu"
  40. :class="[
  41. `${prefixCls}-menu-list__title`,
  42. {
  43. show: openMenu,
  44. },
  45. ]"
  46. >
  47. <span class="text"> {{ title }}</span>
  48. </div>
  49. <ScrollContainer :class="`${prefixCls}-menu-list__content`">
  50. <BasicMenu
  51. :isHorizontal="false"
  52. mode="inline"
  53. :items="chilrenMenus"
  54. :theme="getMenuTheme"
  55. mixSider
  56. @menuClick="handleMenuClick"
  57. />
  58. </ScrollContainer>
  59. <div
  60. v-show="getShowDragBar && openMenu"
  61. :class="`${prefixCls}-drag-bar`"
  62. ref="dragBarRef"
  63. ></div>
  64. </div>
  65. </div>
  66. </template>
  67. <script lang="ts">
  68. import { defineComponent, onMounted, ref, computed, CSSProperties, unref } from 'vue';
  69. import type { Menu } from '/@/router/types';
  70. import type { RouteLocationNormalized } from 'vue-router';
  71. import { useDesign } from '/@/hooks/web/useDesign';
  72. import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus';
  73. import { useI18n } from '/@/hooks/web/useI18n';
  74. import { ScrollContainer } from '/@/components/Container';
  75. import { AppLogo } from '/@/components/Application';
  76. import { useGo } from '/@/hooks/web/usePage';
  77. import { BasicMenu, MenuTag } from '/@/components/Menu';
  78. import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
  79. import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  80. import { useDragLine } from './useLayoutSider';
  81. import clickOutside from '/@/directives/clickOutside';
  82. import { useGlobSetting } from '/@/hooks/setting';
  83. export default defineComponent({
  84. name: 'LayoutMixSider',
  85. components: {
  86. ScrollContainer,
  87. AppLogo,
  88. BasicMenu,
  89. MenuTag,
  90. },
  91. directives: {
  92. clickOutside,
  93. },
  94. setup() {
  95. let menuModules = ref<Menu[]>([]);
  96. const activePath = ref('');
  97. const chilrenMenus = ref<Menu[]>([]);
  98. const openMenu = ref(false);
  99. const dragBarRef = ref<ElRef>(null);
  100. const sideRef = ref<ElRef>(null);
  101. const currentRoute = ref<Nullable<RouteLocationNormalized>>(null);
  102. const { prefixCls } = useDesign('layout-mix-sider');
  103. const go = useGo();
  104. const { t } = useI18n();
  105. const {
  106. getMenuWidth,
  107. getCanDrag,
  108. getCloseMixSidebarOnChange,
  109. getMenuTheme,
  110. } = useMenuSetting();
  111. const { title } = useGlobSetting();
  112. useDragLine(sideRef, dragBarRef, true);
  113. const getMenuStyle = computed(
  114. (): CSSProperties => {
  115. return {
  116. width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
  117. };
  118. }
  119. );
  120. const getShowDragBar = computed(() => unref(getCanDrag));
  121. onMounted(async () => {
  122. menuModules.value = await getShallowMenus();
  123. });
  124. listenerLastChangeTab((route) => {
  125. currentRoute.value = route;
  126. setActive();
  127. if (unref(getCloseMixSidebarOnChange)) {
  128. openMenu.value = false;
  129. }
  130. });
  131. async function hanldeModuleClick(path: string) {
  132. const children = await getChildrenMenus(path);
  133. if (unref(activePath) === path) {
  134. openMenu.value = !unref(openMenu);
  135. if (!unref(openMenu)) {
  136. setActive();
  137. }
  138. } else {
  139. openMenu.value = true;
  140. activePath.value = path;
  141. }
  142. if (!children || children.length === 0) {
  143. go(path);
  144. chilrenMenus.value = [];
  145. openMenu.value = false;
  146. return;
  147. }
  148. chilrenMenus.value = children;
  149. }
  150. async function setActive() {
  151. const path = currentRoute.value?.path;
  152. if (!path) return;
  153. const parentPath = await getCurrentParentPath(path);
  154. activePath.value = parentPath;
  155. // hanldeModuleClick(parentPath);
  156. }
  157. function handleMenuClick(path: string) {
  158. go(path);
  159. }
  160. function handleClickOutside() {
  161. openMenu.value = false;
  162. setActive();
  163. }
  164. return {
  165. t,
  166. prefixCls,
  167. menuModules,
  168. hanldeModuleClick,
  169. activePath,
  170. chilrenMenus,
  171. getShowDragBar,
  172. handleMenuClick,
  173. getMenuStyle,
  174. handleClickOutside,
  175. sideRef,
  176. dragBarRef,
  177. title,
  178. openMenu,
  179. getMenuTheme,
  180. };
  181. },
  182. });
  183. </script>
  184. <style lang="less">
  185. @import (reference) '../../../design/index.less';
  186. @prefix-cls: ~'@{namespace}-layout-mix-sider';
  187. @tag-prefix-cls: ~'@{namespace}-basic-menu-item-tag';
  188. @width: 80px;
  189. .@{prefix-cls} {
  190. position: fixed;
  191. top: 0;
  192. left: 0;
  193. z-index: @layout-mix-sider-fixed-z-index;
  194. width: @width;
  195. height: 100%;
  196. max-width: @width;
  197. min-width: @width;
  198. overflow: hidden;
  199. background: @sider-dark-bg-color;
  200. transition: all 0.2s ease 0s;
  201. flex: 0 0 @width;
  202. .@{tag-prefix-cls} {
  203. position: absolute;
  204. top: 6px;
  205. right: 2px;
  206. }
  207. &-dom {
  208. width: @width;
  209. height: 100%;
  210. max-width: @width;
  211. min-width: @width;
  212. overflow: hidden;
  213. transition: all 0.2s ease 0s;
  214. flex: 0 0 @width;
  215. }
  216. &-logo {
  217. display: flex;
  218. height: @header-height;
  219. padding-left: 0 !important;
  220. justify-content: center;
  221. img {
  222. width: @logo-width;
  223. height: @logo-width;
  224. }
  225. }
  226. &.light {
  227. .@{prefix-cls}-logo {
  228. border-bottom: 1px solid rgb(238, 238, 238);
  229. }
  230. &.open {
  231. > .scroll-container {
  232. border-right: 1px solid rgb(238, 238, 238);
  233. }
  234. }
  235. .@{prefix-cls}-module {
  236. &__item {
  237. font-weight: normal;
  238. color: rgba(0, 0, 0, 0.65);
  239. &--active {
  240. color: @primary-color;
  241. background: unset;
  242. }
  243. }
  244. }
  245. }
  246. @border-color: @sider-dark-lighten-1-bg-color;
  247. &.dark {
  248. &.open {
  249. .@{prefix-cls}-logo {
  250. border-bottom: 1px solid @border-color;
  251. }
  252. > .scroll-container {
  253. border-right: 1px solid @border-color;
  254. }
  255. }
  256. .@{prefix-cls}-menu-list {
  257. background: @sider-dark-bg-color;
  258. &__title {
  259. color: @white;
  260. border-bottom: none;
  261. border-bottom: 1px solid @border-color;
  262. }
  263. }
  264. }
  265. > .scrollbar {
  266. height: calc(100% - @header-height) !important;
  267. }
  268. &-module {
  269. position: relative;
  270. padding-top: 1px;
  271. &__item {
  272. position: relative;
  273. padding: 12px 0;
  274. color: rgba(255, 255, 255, 0.65);
  275. text-align: center;
  276. cursor: pointer;
  277. transition: all 0.3s ease;
  278. &:hover {
  279. color: @white;
  280. }
  281. // &:hover,
  282. &--active {
  283. font-weight: 700;
  284. color: @white;
  285. background: @sider-dark-darken-bg-color;
  286. &::before {
  287. position: absolute;
  288. top: 0;
  289. left: 0;
  290. width: 3px;
  291. height: 100%;
  292. background: @primary-color;
  293. content: '';
  294. }
  295. }
  296. }
  297. &__icon {
  298. margin-bottom: 8px;
  299. font-size: 24px;
  300. }
  301. &__name {
  302. margin-bottom: 0;
  303. font-size: 12px;
  304. }
  305. }
  306. &-menu-list {
  307. position: fixed;
  308. top: 0;
  309. left: 80px;
  310. width: 0;
  311. width: 200px;
  312. height: calc(100%);
  313. background: #fff;
  314. transition: width 0.2s;
  315. .@{tag-prefix-cls} {
  316. position: absolute;
  317. top: 10px;
  318. right: 30px;
  319. }
  320. &__title {
  321. display: flex;
  322. height: @header-height;
  323. margin-left: -6px;
  324. font-size: 18px;
  325. color: @primary-color;
  326. border-bottom: 1px solid rgb(238, 238, 238);
  327. opacity: 0;
  328. transition: unset;
  329. // justify-content: center;
  330. align-items: center;
  331. justify-content: start;
  332. &.show {
  333. opacity: 1;
  334. transition: all 0.5s ease;
  335. }
  336. }
  337. &__content {
  338. height: calc(100% - @header-height) !important;
  339. .scrollbar__wrap {
  340. height: 100%;
  341. overflow-x: hidden;
  342. }
  343. .scrollbar__bar.is-horizontal {
  344. display: none;
  345. }
  346. .ant-menu {
  347. height: 100%;
  348. }
  349. .ant-menu-inline,
  350. .ant-menu-vertical,
  351. .ant-menu-vertical-left {
  352. border-right: 1px solid transparent;
  353. }
  354. }
  355. }
  356. &-drag-bar {
  357. position: absolute;
  358. top: 0;
  359. right: -3px;
  360. width: 3px;
  361. height: 100%;
  362. cursor: ew-resize;
  363. background: #f8f8f9;
  364. border-top: none;
  365. border-bottom: none;
  366. box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
  367. }
  368. }
  369. </style>