MixSider.vue 14.5 KB
Newer Older
V
vben 已提交
1
<template>
V
vben 已提交
2
  <div :class="`${prefixCls}-dom`" :style="getDomStyle"></div>
V
vben 已提交
3 4
  <div
    v-click-outside="handleClickOutside"
V
vben 已提交
5
    :style="getWrapStyle"
V
vben 已提交
6 7 8 9 10
    :class="[
      prefixCls,
      getMenuTheme,
      {
        open: openMenu,
V
vben 已提交
11
        mini: getCollapsed,
V
vben 已提交
12 13
      },
    ]"
V
vben 已提交
14
    v-bind="getMenuEvents"
V
vben 已提交
15 16
  >
    <AppLogo :showTitle="false" :class="`${prefixCls}-logo`" />
V
vben 已提交
17

18
    <LayoutTrigger :class="`${prefixCls}-trigger`" />
V
vben 已提交
19

V
vben 已提交
20 21 22 23 24 25 26 27 28
    <ScrollContainer>
      <ul :class="`${prefixCls}-module`">
        <li
          :class="[
            `${prefixCls}-module__item `,
            {
              [`${prefixCls}-module__item--active`]: item.path === activePath,
            },
          ]"
29
          v-bind="getItemEvents(item)"
V
vben 已提交
30 31 32
          v-for="item in menuModules"
          :key="item.path"
        >
33
          <SimpleMenuTag :item="item" collapseParent dot />
34
          <Icon
V
vben 已提交
35
            :class="`${prefixCls}-module__icon`"
V
vben 已提交
36
            :size="getCollapsed ? 16 : 20"
37
            :icon="item.icon || (item.meta && item.meta.icon)"
V
vben 已提交
38
          />
V
vben 已提交
39 40 41
          <p :class="`${prefixCls}-module__name`">
            {{ t(item.name) }}
          </p>
V
vben 已提交
42 43 44 45 46 47
        </li>
      </ul>
    </ScrollContainer>

    <div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
      <div
V
vben 已提交
48
        v-show="openMenu"
V
vben 已提交
49 50 51 52 53 54 55 56
        :class="[
          `${prefixCls}-menu-list__title`,
          {
            show: openMenu,
          },
        ]"
      >
        <span class="text"> {{ title }}</span>
57 58
        <Icon
          :size="16"
V
vben 已提交
59
          :icon="getMixSideFixed ? 'ri:pushpin-2-fill' : 'ri:pushpin-2-line'"
60 61 62
          class="pushpin"
          @click="handleFixedMenu"
        />
V
vben 已提交
63 64
      </div>
      <ScrollContainer :class="`${prefixCls}-menu-list__content`">
65
        <SimpleMenu
66
          :items="childrenMenus"
V
vben 已提交
67
          :theme="getMenuTheme"
68
          mixSider
V
vben 已提交
69
          @menu-click="handleMenuClick"
V
vben 已提交
70 71 72 73 74 75 76 77 78 79 80 81
        />
      </ScrollContainer>
      <div
        v-show="getShowDragBar && openMenu"
        :class="`${prefixCls}-drag-bar`"
        ref="dragBarRef"
      ></div>
    </div>
  </div>
</template>
<script lang="ts">
  import type { Menu } from '/@/router/types';
V
vben 已提交
82
  import type { CSSProperties } from 'vue';
83
  import { computed, defineComponent, onMounted, ref, unref, watch } from 'vue';
V
vben 已提交
84
  import type { RouteLocationNormalized } from 'vue-router';
V
vben 已提交
85
  import { ScrollContainer } from '/@/components/Container';
86
  import { SimpleMenu, SimpleMenuTag } from '/@/components/SimpleMenu';
87
  import { Icon } from '/@/components/Icon';
V
vben 已提交
88 89
  import { AppLogo } from '/@/components/Application';
  import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
90
  import { usePermissionStore } from '/@/store/modules/permission';
V
vben 已提交
91
  import { useDragLine } from './useLayoutSider';
92
  import { useGlobSetting } from '/@/hooks/setting';
V
vben 已提交
93 94 95
  import { useDesign } from '/@/hooks/web/useDesign';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useGo } from '/@/hooks/web/usePage';
96
  import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
V
vben 已提交
97
  import clickOutside from '/@/directives/clickOutside';
98
  import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '/@/router/menus';
V
Vben 已提交
99
  import { listenerRouteChange } from '/@/logics/mitt/routeChange';
100
  import LayoutTrigger from '../trigger/index.vue';
V
vben 已提交
101 102 103 104 105 106

  export default defineComponent({
    name: 'LayoutMixSider',
    components: {
      ScrollContainer,
      AppLogo,
107
      SimpleMenu,
108
      Icon,
109
      LayoutTrigger,
110
      SimpleMenuTag,
V
vben 已提交
111 112 113 114 115 116 117
    },
    directives: {
      clickOutside,
    },
    setup() {
      let menuModules = ref<Menu[]>([]);
      const activePath = ref('');
118
      const childrenMenus = ref<Menu[]>([]);
V
vben 已提交
119
      const openMenu = ref(false);
V
vben 已提交
120 121 122
      const dragBarRef = ref(null);
      const sideRef = ref(null);
      const currentRoute = ref<RouteLocationNormalized | null>(null);
V
vben 已提交
123 124 125 126 127 128 129 130 131

      const { prefixCls } = useDesign('layout-mix-sider');
      const go = useGo();
      const { t } = useI18n();
      const {
        getMenuWidth,
        getCanDrag,
        getCloseMixSidebarOnChange,
        getMenuTheme,
V
vben 已提交
132
        getMixSideTrigger,
133 134 135 136
        getRealWidth,
        getMixSideFixed,
        mixSideHasChildren,
        setMenuSetting,
V
vben 已提交
137 138
        getIsMixSidebar,
        getCollapsed,
V
vben 已提交
139
      } = useMenuSetting();
140

V
vben 已提交
141
      const { title } = useGlobSetting();
142
      const permissionStore = usePermissionStore();
V
vben 已提交
143 144 145

      useDragLine(sideRef, dragBarRef, true);

V
Vben 已提交
146 147 148 149 150 151
      const getMenuStyle = computed((): CSSProperties => {
        return {
          width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
          left: `${unref(getMixSideWidth)}px`,
        };
      });
V
vben 已提交
152

153
      const getIsFixed = computed(() => {
V
vben 已提交
154
        /* eslint-disable-next-line */
155
        mixSideHasChildren.value = unref(childrenMenus).length > 0;
156 157
        const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren);
        if (isFixed) {
V
vben 已提交
158
          /* eslint-disable-next-line */
159 160 161 162 163
          openMenu.value = true;
        }
        return isFixed;
      });

V
vben 已提交
164 165 166 167
      const getMixSideWidth = computed(() => {
        return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH;
      });

V
Vben 已提交
168 169 170 171 172
      const getDomStyle = computed((): CSSProperties => {
        const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0;
        const width = `${unref(getMixSideWidth) + fixedWidth}px`;
        return getWrapCommonStyle(width);
      });
V
vben 已提交
173

V
Vben 已提交
174 175 176 177
      const getWrapStyle = computed((): CSSProperties => {
        const width = `${unref(getMixSideWidth)}px`;
        return getWrapCommonStyle(width);
      });
178

V
vben 已提交
179
      const getMenuEvents = computed(() => {
V
vben 已提交
180 181 182
        return !unref(getMixSideFixed)
          ? {
              onMouseleave: () => {
183
                setActive(true);
V
vben 已提交
184 185 186 187
                closeMenu();
              },
            }
          : {};
V
vben 已提交
188 189
      });

V
vben 已提交
190 191 192 193 194 195
      const getShowDragBar = computed(() => unref(getCanDrag));

      onMounted(async () => {
        menuModules.value = await getShallowMenus();
      });

196 197 198 199 200 201 202 203 204 205 206
      // Menu changes
      watch(
        [() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
        async () => {
          menuModules.value = await getShallowMenus();
        },
        {
          immediate: true,
        },
      );

V
Vben 已提交
207
      listenerRouteChange((route) => {
V
vben 已提交
208
        currentRoute.value = route;
209
        setActive(true);
V
vben 已提交
210
        if (unref(getCloseMixSidebarOnChange)) {
211
          closeMenu();
V
vben 已提交
212 213 214
        }
      });

V
vben 已提交
215 216 217 218 219 220 221 222 223 224
      function getWrapCommonStyle(width: string): CSSProperties {
        return {
          width,
          maxWidth: width,
          minWidth: width,
          flex: `0 0 ${width}`,
        };
      }

      // Process module menu click
225
      async function handleModuleClick(path: string, hover = false) {
V
vben 已提交
226 227
        const children = await getChildrenMenus(path);
        if (unref(activePath) === path) {
V
vben 已提交
228
          if (!hover) {
229 230 231 232 233
            if (!unref(openMenu)) {
              openMenu.value = true;
            } else {
              closeMenu();
            }
234 235 236 237
          } else {
            if (!unref(openMenu)) {
              openMenu.value = true;
            }
V
vben 已提交
238
          }
V
vben 已提交
239 240 241 242 243 244 245 246 247
          if (!unref(openMenu)) {
            setActive();
          }
        } else {
          openMenu.value = true;
          activePath.value = path;
        }

        if (!children || children.length === 0) {
248 249
          if (!hover) go(path);
          childrenMenus.value = [];
250
          closeMenu();
V
vben 已提交
251 252
          return;
        }
253
        childrenMenus.value = children;
V
vben 已提交
254 255
      }

V
vben 已提交
256
      // Set the currently active menu and submenu
257
      async function setActive(setChildren = false) {
V
vben 已提交
258 259
        const path = currentRoute.value?.path;
        if (!path) return;
260
        activePath.value = await getCurrentParentPath(path);
V
vben 已提交
261
        // hanldeModuleClick(parentPath);
V
vben 已提交
262
        if (unref(getIsMixSidebar)) {
263 264 265 266 267
          const activeMenu = unref(menuModules).find((item) => item.path === unref(activePath));
          const p = activeMenu?.path;
          if (p) {
            const children = await getChildrenMenus(p);
            if (setChildren) {
268
              childrenMenus.value = children;
V
vben 已提交
269 270 271 272

              if (unref(getMixSideFixed)) {
                openMenu.value = children.length > 0;
              }
273 274
            }
            if (children.length === 0) {
275
              childrenMenus.value = [];
276 277 278
            }
          }
        }
V
vben 已提交
279 280 281 282 283 284 285
      }

      function handleMenuClick(path: string) {
        go(path);
      }

      function handleClickOutside() {
V
vben 已提交
286
        setActive(true);
287
        closeMenu();
V
vben 已提交
288 289
      }

V
vben 已提交
290 291 292
      function getItemEvents(item: Menu) {
        if (unref(getMixSideTrigger) === 'hover') {
          return {
293 294 295 296 297
            onMouseenter: () => handleModuleClick(item.path, true),
            onClick: async () => {
              const children = await getChildrenMenus(item.path);
              if (item.path && (!children || children.length === 0)) go(item.path);
            },
V
vben 已提交
298 299 300
          };
        }
        return {
301
          onClick: () => handleModuleClick(item.path),
V
vben 已提交
302 303 304
        };
      }

305 306 307 308 309 310
      function handleFixedMenu() {
        setMenuSetting({
          mixSideFixed: !unref(getIsFixed),
        });
      }

V
vben 已提交
311
      // Close menu
312 313 314 315 316 317
      function closeMenu() {
        if (!unref(getIsFixed)) {
          openMenu.value = false;
        }
      }

V
vben 已提交
318 319 320 321
      return {
        t,
        prefixCls,
        menuModules,
322
        handleModuleClick: handleModuleClick,
V
vben 已提交
323
        activePath,
324
        childrenMenus: childrenMenus,
V
vben 已提交
325 326 327 328 329 330 331 332 333
        getShowDragBar,
        handleMenuClick,
        getMenuStyle,
        handleClickOutside,
        sideRef,
        dragBarRef,
        title,
        openMenu,
        getMenuTheme,
V
vben 已提交
334 335
        getItemEvents,
        getMenuEvents,
336 337 338
        getDomStyle,
        handleFixedMenu,
        getMixSideFixed,
V
vben 已提交
339 340
        getWrapStyle,
        getCollapsed,
V
vben 已提交
341 342 343 344 345 346 347 348 349
      };
    },
  });
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-layout-mix-sider';
  @width: 80px;
  .@{prefix-cls} {
    position: fixed;
V
vben 已提交
350
    z-index: @layout-mix-sider-fixed-z-index;
V
vben 已提交
351 352 353 354
    top: 0;
    left: 0;
    height: 100%;
    overflow: hidden;
V
vben 已提交
355
    transition: all 0.2s ease 0s;
V
vben 已提交
356
    background-color: @sider-dark-bg-color;
357

V
vben 已提交
358 359 360 361 362 363 364 365
    &-dom {
      height: 100%;
      overflow: hidden;
      transition: all 0.2s ease 0s;
    }

    &-logo {
      display: flex;
V
vben 已提交
366
      justify-content: center;
V
vben 已提交
367 368 369 370 371 372 373 374 375 376 377
      height: @header-height;
      padding-left: 0 !important;

      img {
        width: @logo-width;
        height: @logo-width;
      }
    }

    &.light {
      .@{prefix-cls}-logo {
V
vben 已提交
378
        border-bottom: 1px solid rgb(238 238 238);
V
vben 已提交
379 380 381
      }

      &.open {
V
vben 已提交
382
        > .scrollbar {
V
vben 已提交
383
          border-right: 1px solid rgb(238 238 238);
V
vben 已提交
384 385 386 387 388
        }
      }

      .@{prefix-cls}-module {
        &__item {
V
vben 已提交
389
          color: rgb(0 0 0 / 65%);
V
vben 已提交
390
          font-weight: normal;
V
vben 已提交
391 392

          &--active {
V
Vben 已提交
393
            background-color: unset;
V
vben 已提交
394
            color: @primary-color;
V
vben 已提交
395 396 397
          }
        }
      }
398
      .@{prefix-cls}-menu-list {
399
        &__content {
V
vben 已提交
400
          box-shadow: 0 0 4px 0 rgb(0 0 0 / 10%);
401 402
        }

403 404
        &__title {
          .pushpin {
V
vben 已提交
405
            color: rgb(0 0 0 / 35%);
406 407

            &:hover {
V
vben 已提交
408
              color: rgb(0 0 0 / 85%);
409 410 411 412
            }
          }
        }
      }
V
vben 已提交
413
    }
V
Vben 已提交
414
    @border-color: @sider-dark-lighten-bg-color;
V
vben 已提交
415 416 417 418

    &.dark {
      &.open {
        .@{prefix-cls}-logo {
419
          // border-bottom: 1px solid @border-color;
V
vben 已提交
420 421
        }

V
vben 已提交
422
        > .scrollbar {
V
vben 已提交
423
          border-right: 1px solid @border-color;
V
vben 已提交
424 425 426
        }
      }
      .@{prefix-cls}-menu-list {
V
Vben 已提交
427
        background-color: @sider-dark-bg-color;
V
vben 已提交
428 429 430

        &__title {
          border-bottom: none;
V
vben 已提交
431
          border-bottom: 1px solid @border-color;
V
vben 已提交
432
          color: @white;
V
vben 已提交
433 434 435 436
        }
      }
    }

437
    > .scrollbar {
V
vben 已提交
438
      height: calc(100% - @header-height - 38px);
439 440
    }

V
vben 已提交
441 442 443 444 445 446 447 448 449 450
    &.mini &-module {
      &__name {
        display: none;
      }

      &__icon {
        margin-bottom: 0;
      }
    }

V
vben 已提交
451 452 453 454 455 456 457
    &-module {
      position: relative;
      padding-top: 1px;

      &__item {
        position: relative;
        padding: 12px 0;
V
vben 已提交
458
        transition: all 0.3s ease;
V
vben 已提交
459
        color: rgb(255 255 255 / 65%);
V
vben 已提交
460 461 462 463 464 465 466 467
        text-align: center;
        cursor: pointer;

        &:hover {
          color: @white;
        }
        // &:hover,
        &--active {
V
Vben 已提交
468
          background-color: @sider-dark-darken-bg-color;
V
vben 已提交
469 470
          color: @white;
          font-weight: 700;
V
vben 已提交
471 472

          &::before {
V
vben 已提交
473
            content: '';
V
vben 已提交
474 475 476 477 478
            position: absolute;
            top: 0;
            left: 0;
            width: 3px;
            height: 100%;
V
Vben 已提交
479
            background-color: @primary-color;
V
vben 已提交
480 481 482 483 484 485
          }
        }
      }

      &__icon {
        margin-bottom: 8px;
V
vben 已提交
486
        transition: all 0.2s;
V
vben 已提交
487
        font-size: 24px;
V
vben 已提交
488 489 490 491
      }

      &__name {
        margin-bottom: 0;
V
vben 已提交
492
        transition: all 0.2s;
V
vben 已提交
493
        font-size: 12px;
V
vben 已提交
494 495 496
      }
    }

V
vben 已提交
497 498 499 500 501
    &-trigger {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
V
vben 已提交
502 503
      height: 36px;
      background-color: @trigger-dark-bg-color;
V
vben 已提交
504
      color: rgb(255 255 255 / 65%);
V
vben 已提交
505 506
      font-size: 14px;
      line-height: 36px;
507
      text-align: center;
V
vben 已提交
508 509 510 511
      cursor: pointer;
    }

    &.light &-trigger {
512
      border-top: 1px solid #eee;
V
vben 已提交
513 514
      background-color: #fff;
      color: rgb(0 0 0 / 65%);
V
vben 已提交
515 516
    }

V
vben 已提交
517 518 519 520 521
    &-menu-list {
      position: fixed;
      top: 0;
      width: 200px;
      height: calc(100%);
V
vben 已提交
522
      transition: all 0.2s;
V
vben 已提交
523
      background-color: #fff;
V
vben 已提交
524 525 526

      &__title {
        display: flex;
V
vben 已提交
527 528
        align-items: center;
        justify-content: space-between;
V
vben 已提交
529
        height: @header-height;
V
vben 已提交
530 531 532 533
        transition: unset;
        border-bottom: 1px solid rgb(238 238 238);
        opacity: 0;
        color: @primary-color;
534
        // margin-left: -6px;
V
vben 已提交
535 536 537
        font-size: 18px;

        &.show {
538
          min-width: 130px;
V
vben 已提交
539
          transition: all 0.5s ease;
V
vben 已提交
540
          opacity: 1;
V
vben 已提交
541
        }
542 543 544

        .pushpin {
          margin-right: 6px;
V
vben 已提交
545
          color: rgb(255 255 255 / 65%);
546 547 548 549 550 551
          cursor: pointer;

          &:hover {
            color: #fff;
          }
        }
V
vben 已提交
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
      }

      &__content {
        height: calc(100% - @header-height) !important;

        .scrollbar__wrap {
          height: 100%;
          overflow-x: hidden;
        }

        .scrollbar__bar.is-horizontal {
          display: none;
        }

        .ant-menu {
          height: 100%;
        }

        .ant-menu-inline,
        .ant-menu-vertical,
        .ant-menu-vertical-left {
          border-right: 1px solid transparent;
        }
      }
    }

    &-drag-bar {
      position: absolute;
580 581 582 583
      top: 50px;
      right: -1px;
      width: 1px;
      height: calc(100% - 50px);
V
vben 已提交
584 585
      border-top: none;
      border-bottom: none;
V
vben 已提交
586
      background-color: #f8f8f9;
V
vben 已提交
587
      box-shadow: 0 0 4px 0 rgb(28 36 56 / 15%);
V
vben 已提交
588
      cursor: ew-resize;
V
vben 已提交
589 590 591
    }
  }
</style>