vben
authored
|
1
|
<template>
|
vben
authored
|
2
|
<div :class="`${prefixCls}-dom`" :style="getDomStyle"></div>
|
vben
authored
|
3
4
|
<div
v-click-outside="handleClickOutside"
|
vben
authored
|
5
|
:style="getWrapStyle"
|
vben
authored
|
6
7
8
9
10
|
:class="[
prefixCls,
getMenuTheme,
{
open: openMenu,
|
vben
authored
|
11
|
mini: getCollapsed,
|
vben
authored
|
12
13
|
},
]"
|
vben
authored
|
14
|
v-bind="getMenuEvents"
|
vben
authored
|
15
16
|
>
<AppLogo :showTitle="false" :class="`${prefixCls}-logo`" />
|
vben
authored
|
17
|
|
|
18
|
<LayoutTrigger :class="`${prefixCls}-trigger`" />
|
vben
authored
|
19
|
|
vben
authored
|
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,
},
]"
|
Vben
authored
|
29
|
v-bind="getItemEvents(item)"
|
vben
authored
|
30
31
32
|
v-for="item in menuModules"
:key="item.path"
>
|
Vben
authored
|
33
|
<SimpleMenuTag :item="item" collapseParent dot />
|
vben
authored
|
34
|
<Icon
|
vben
authored
|
35
|
:class="`${prefixCls}-module__icon`"
|
vben
authored
|
36
|
:size="getCollapsed ? 16 : 20"
|
Vben
authored
|
37
|
:icon="item.icon || (item.meta && item.meta.icon)"
|
vben
authored
|
38
|
/>
|
vben
authored
|
39
40
41
|
<p :class="`${prefixCls}-module__name`">
{{ t(item.name) }}
</p>
|
vben
authored
|
42
43
44
45
46
47
|
</li>
</ul>
</ScrollContainer>
<div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
<div
|
vben
authored
|
48
|
v-show="openMenu"
|
vben
authored
|
49
50
51
52
53
54
55
56
|
:class="[
`${prefixCls}-menu-list__title`,
{
show: openMenu,
},
]"
>
<span class="text"> {{ title }}</span>
|
vben
authored
|
57
58
|
<Icon
:size="16"
|
vben
authored
|
59
|
:icon="getMixSideFixed ? 'ri:pushpin-2-fill' : 'ri:pushpin-2-line'"
|
vben
authored
|
60
61
62
|
class="pushpin"
@click="handleFixedMenu"
/>
|
vben
authored
|
63
64
|
</div>
<ScrollContainer :class="`${prefixCls}-menu-list__content`">
|
vben
authored
|
65
|
<SimpleMenu
|
|
66
|
:items="childrenMenus"
|
vben
authored
|
67
|
:theme="getMenuTheme"
|
vben
authored
|
68
|
mixSider
|
vben
authored
|
69
|
@menu-click="handleMenuClick"
|
vben
authored
|
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';
|
vben
authored
|
82
|
import type { CSSProperties } from 'vue';
|
|
83
|
import { computed, defineComponent, onMounted, ref, unref, watch } from 'vue';
|
vben
authored
|
84
|
import type { RouteLocationNormalized } from 'vue-router';
|
vben
authored
|
85
|
import { ScrollContainer } from '/@/components/Container';
|
|
86
|
import { SimpleMenu, SimpleMenuTag } from '/@/components/SimpleMenu';
|
Vben
authored
|
87
|
import { Icon } from '/@/components/Icon';
|
vben
authored
|
88
89
|
import { AppLogo } from '/@/components/Application';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
|
90
|
import { usePermissionStore } from '/@/store/modules/permission';
|
vben
authored
|
91
|
import { useDragLine } from './useLayoutSider';
|
vben
authored
|
92
|
import { useGlobSetting } from '/@/hooks/setting';
|
vben
authored
|
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';
|
vben
authored
|
97
|
import clickOutside from '/@/directives/clickOutside';
|
|
98
|
import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '/@/router/menus';
|
Vben
authored
|
99
|
import { listenerRouteChange } from '/@/logics/mitt/routeChange';
|
|
100
|
import LayoutTrigger from '../trigger/index.vue';
|
vben
authored
|
101
102
103
104
105
106
|
export default defineComponent({
name: 'LayoutMixSider',
components: {
ScrollContainer,
AppLogo,
|
vben
authored
|
107
|
SimpleMenu,
|
vben
authored
|
108
|
Icon,
|
|
109
|
LayoutTrigger,
|
Vben
authored
|
110
|
SimpleMenuTag,
|
vben
authored
|
111
112
113
114
115
116
117
|
},
directives: {
clickOutside,
},
setup() {
let menuModules = ref<Menu[]>([]);
const activePath = ref('');
|
|
118
|
const childrenMenus = ref<Menu[]>([]);
|
vben
authored
|
119
120
121
122
123
124
125
126
127
128
129
130
131
|
const openMenu = ref(false);
const dragBarRef = ref<ElRef>(null);
const sideRef = ref<ElRef>(null);
const currentRoute = ref<Nullable<RouteLocationNormalized>>(null);
const { prefixCls } = useDesign('layout-mix-sider');
const go = useGo();
const { t } = useI18n();
const {
getMenuWidth,
getCanDrag,
getCloseMixSidebarOnChange,
getMenuTheme,
|
vben
authored
|
132
|
getMixSideTrigger,
|
vben
authored
|
133
134
135
136
|
getRealWidth,
getMixSideFixed,
mixSideHasChildren,
setMenuSetting,
|
vben
authored
|
137
138
|
getIsMixSidebar,
getCollapsed,
|
vben
authored
|
139
|
} = useMenuSetting();
|
vben
authored
|
140
|
|
vben
authored
|
141
|
const { title } = useGlobSetting();
|
|
142
|
const permissionStore = usePermissionStore();
|
vben
authored
|
143
144
145
|
useDragLine(sideRef, dragBarRef, true);
|
Vben
authored
|
146
147
148
149
150
151
|
const getMenuStyle = computed((): CSSProperties => {
return {
width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
left: `${unref(getMixSideWidth)}px`,
};
});
|
vben
authored
|
152
|
|
vben
authored
|
153
|
const getIsFixed = computed(() => {
|
vben
authored
|
154
|
/* eslint-disable-next-line */
|
|
155
|
mixSideHasChildren.value = unref(childrenMenus).length > 0;
|
vben
authored
|
156
157
|
const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren);
if (isFixed) {
|
vben
authored
|
158
|
/* eslint-disable-next-line */
|
vben
authored
|
159
160
161
162
163
|
openMenu.value = true;
}
return isFixed;
});
|
vben
authored
|
164
165
166
167
|
const getMixSideWidth = computed(() => {
return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH;
});
|
Vben
authored
|
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);
});
|
vben
authored
|
173
|
|
Vben
authored
|
174
175
176
177
|
const getWrapStyle = computed((): CSSProperties => {
const width = `${unref(getMixSideWidth)}px`;
return getWrapCommonStyle(width);
});
|
vben
authored
|
178
|
|
vben
authored
|
179
|
const getMenuEvents = computed(() => {
|
vben
authored
|
180
181
182
|
return !unref(getMixSideFixed)
? {
onMouseleave: () => {
|
|
183
|
setActive(true);
|
vben
authored
|
184
185
186
187
|
closeMenu();
},
}
: {};
|
vben
authored
|
188
189
|
});
|
vben
authored
|
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,
},
);
|
Vben
authored
|
207
|
listenerRouteChange((route) => {
|
vben
authored
|
208
|
currentRoute.value = route;
|
vben
authored
|
209
|
setActive(true);
|
vben
authored
|
210
|
if (unref(getCloseMixSidebarOnChange)) {
|
vben
authored
|
211
|
closeMenu();
|
vben
authored
|
212
213
214
|
}
});
|
vben
authored
|
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) {
|
vben
authored
|
226
227
|
const children = await getChildrenMenus(path);
if (unref(activePath) === path) {
|
vben
authored
|
228
|
if (!hover) {
|
vben
authored
|
229
230
231
232
233
|
if (!unref(openMenu)) {
openMenu.value = true;
} else {
closeMenu();
}
|
|
234
235
236
237
|
} else {
if (!unref(openMenu)) {
openMenu.value = true;
}
|
vben
authored
|
238
|
}
|
vben
authored
|
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 = [];
|
vben
authored
|
250
|
closeMenu();
|
vben
authored
|
251
252
|
return;
}
|
|
253
|
childrenMenus.value = children;
|
vben
authored
|
254
255
|
}
|
vben
authored
|
256
|
// Set the currently active menu and submenu
|
vben
authored
|
257
|
async function setActive(setChildren = false) {
|
vben
authored
|
258
259
|
const path = currentRoute.value?.path;
if (!path) return;
|
|
260
|
activePath.value = await getCurrentParentPath(path);
|
vben
authored
|
261
|
// hanldeModuleClick(parentPath);
|
vben
authored
|
262
|
if (unref(getIsMixSidebar)) {
|
vben
authored
|
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;
|
vben
authored
|
269
270
271
272
|
if (unref(getMixSideFixed)) {
openMenu.value = children.length > 0;
}
|
vben
authored
|
273
274
|
}
if (children.length === 0) {
|
|
275
|
childrenMenus.value = [];
|
vben
authored
|
276
277
278
|
}
}
}
|
vben
authored
|
279
280
281
282
283
284
285
|
}
function handleMenuClick(path: string) {
go(path);
}
function handleClickOutside() {
|
vben
authored
|
286
|
setActive(true);
|
vben
authored
|
287
|
closeMenu();
|
vben
authored
|
288
289
|
}
|
vben
authored
|
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);
},
|
vben
authored
|
298
299
300
|
};
}
return {
|
|
301
|
onClick: () => handleModuleClick(item.path),
|
vben
authored
|
302
303
304
|
};
}
|
vben
authored
|
305
306
307
308
309
310
|
function handleFixedMenu() {
setMenuSetting({
mixSideFixed: !unref(getIsFixed),
});
}
|
vben
authored
|
311
|
// Close menu
|
vben
authored
|
312
313
314
315
316
317
|
function closeMenu() {
if (!unref(getIsFixed)) {
openMenu.value = false;
}
}
|
vben
authored
|
318
319
320
321
|
return {
t,
prefixCls,
menuModules,
|
|
322
|
handleModuleClick: handleModuleClick,
|
vben
authored
|
323
|
activePath,
|
|
324
|
childrenMenus: childrenMenus,
|
vben
authored
|
325
326
327
328
329
330
331
332
333
|
getShowDragBar,
handleMenuClick,
getMenuStyle,
handleClickOutside,
sideRef,
dragBarRef,
title,
openMenu,
getMenuTheme,
|
vben
authored
|
334
335
|
getItemEvents,
getMenuEvents,
|
vben
authored
|
336
337
338
|
getDomStyle,
handleFixedMenu,
getMixSideFixed,
|
vben
authored
|
339
340
|
getWrapStyle,
getCollapsed,
|
vben
authored
|
341
342
343
344
345
346
347
348
349
350
351
352
353
354
|
};
},
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-layout-mix-sider';
@width: 80px;
.@{prefix-cls} {
position: fixed;
top: 0;
left: 0;
z-index: @layout-mix-sider-fixed-z-index;
height: 100%;
overflow: hidden;
|
Vben
authored
|
355
|
background-color: @sider-dark-bg-color;
|
vben
authored
|
356
|
transition: all 0.2s ease 0s;
|
vben
authored
|
357
|
|
vben
authored
|
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
&-dom {
height: 100%;
overflow: hidden;
transition: all 0.2s ease 0s;
}
&-logo {
display: flex;
height: @header-height;
padding-left: 0 !important;
justify-content: center;
img {
width: @logo-width;
height: @logo-width;
}
}
&.light {
.@{prefix-cls}-logo {
|
vben
authored
|
378
|
border-bottom: 1px solid rgb(238 238 238);
|
vben
authored
|
379
380
381
|
}
&.open {
|
vben
authored
|
382
|
> .scrollbar {
|
vben
authored
|
383
|
border-right: 1px solid rgb(238 238 238);
|
vben
authored
|
384
385
386
387
388
389
|
}
}
.@{prefix-cls}-module {
&__item {
font-weight: normal;
|
vben
authored
|
390
|
color: rgb(0 0 0 / 65%);
|
vben
authored
|
391
392
393
|
&--active {
color: @primary-color;
|
Vben
authored
|
394
|
background-color: unset;
|
vben
authored
|
395
396
397
|
}
}
}
|
vben
authored
|
398
|
.@{prefix-cls}-menu-list {
|
vben
authored
|
399
|
&__content {
|
vben
authored
|
400
|
box-shadow: 0 0 4px 0 rgb(0 0 0 / 10%);
|
vben
authored
|
401
402
|
}
|
vben
authored
|
403
404
|
&__title {
.pushpin {
|
vben
authored
|
405
|
color: rgb(0 0 0 / 35%);
|
vben
authored
|
406
407
|
&:hover {
|
vben
authored
|
408
|
color: rgb(0 0 0 / 85%);
|
vben
authored
|
409
410
411
412
|
}
}
}
}
|
vben
authored
|
413
|
}
|
Vben
authored
|
414
|
@border-color: @sider-dark-lighten-bg-color;
|
vben
authored
|
415
416
417
418
|
&.dark {
&.open {
.@{prefix-cls}-logo {
|
Vben
authored
|
419
|
// border-bottom: 1px solid @border-color;
|
vben
authored
|
420
421
|
}
|
vben
authored
|
422
|
> .scrollbar {
|
vben
authored
|
423
|
border-right: 1px solid @border-color;
|
vben
authored
|
424
425
426
|
}
}
.@{prefix-cls}-menu-list {
|
Vben
authored
|
427
|
background-color: @sider-dark-bg-color;
|
vben
authored
|
428
429
430
431
|
&__title {
color: @white;
border-bottom: none;
|
vben
authored
|
432
|
border-bottom: 1px solid @border-color;
|
vben
authored
|
433
434
435
436
|
}
}
}
|
vben
authored
|
437
|
> .scrollbar {
|
vben
authored
|
438
|
height: calc(100% - @header-height - 38px);
|
vben
authored
|
439
440
|
}
|
vben
authored
|
441
442
443
444
445
446
447
448
449
450
|
&.mini &-module {
&__name {
display: none;
}
&__icon {
margin-bottom: 0;
}
}
|
vben
authored
|
451
452
453
454
455
456
457
|
&-module {
position: relative;
padding-top: 1px;
&__item {
position: relative;
padding: 12px 0;
|
vben
authored
|
458
|
color: rgb(255 255 255 / 65%);
|
vben
authored
|
459
460
461
462
463
464
465
466
467
468
469
|
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: @white;
}
// &:hover,
&--active {
font-weight: 700;
color: @white;
|
Vben
authored
|
470
|
background-color: @sider-dark-darken-bg-color;
|
vben
authored
|
471
472
473
474
475
476
477
|
&::before {
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 100%;
|
Vben
authored
|
478
|
background-color: @primary-color;
|
vben
authored
|
479
480
481
482
483
484
485
486
|
content: '';
}
}
}
&__icon {
margin-bottom: 8px;
font-size: 24px;
|
vben
authored
|
487
|
transition: all 0.2s;
|
vben
authored
|
488
489
490
491
492
|
}
&__name {
margin-bottom: 0;
font-size: 12px;
|
vben
authored
|
493
|
transition: all 0.2s;
|
vben
authored
|
494
495
496
|
}
}
|
vben
authored
|
497
498
499
500
501
|
&-trigger {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
|
|
502
|
font-size: 14px;
|
vben
authored
|
503
|
color: rgb(255 255 255 / 65%);
|
|
504
|
text-align: center;
|
vben
authored
|
505
|
cursor: pointer;
|
|
506
507
508
|
background-color: @trigger-dark-bg-color;
height: 36px;
line-height: 36px;
|
vben
authored
|
509
510
511
|
}
&.light &-trigger {
|
vben
authored
|
512
|
color: rgb(0 0 0 / 65%);
|
Vben
authored
|
513
|
background-color: #fff;
|
|
514
|
border-top: 1px solid #eee;
|
vben
authored
|
515
516
|
}
|
vben
authored
|
517
518
519
520
521
|
&-menu-list {
position: fixed;
top: 0;
width: 200px;
height: calc(100%);
|
Vben
authored
|
522
|
background-color: #fff;
|
vben
authored
|
523
|
transition: all 0.2s;
|
vben
authored
|
524
525
526
527
|
&__title {
display: flex;
height: @header-height;
|
vben
authored
|
528
|
// margin-left: -6px;
|
vben
authored
|
529
530
|
font-size: 18px;
color: @primary-color;
|
vben
authored
|
531
532
|
border-bottom: 1px solid rgb(238 238 238);
opacity: 0%;
|
vben
authored
|
533
534
|
transition: unset;
align-items: center;
|
vben
authored
|
535
|
justify-content: space-between;
|
vben
authored
|
536
537
|
&.show {
|
vben
authored
|
538
|
min-width: 130px;
|
vben
authored
|
539
|
opacity: 100%;
|
vben
authored
|
540
541
|
transition: all 0.5s ease;
}
|
vben
authored
|
542
543
544
|
.pushpin {
margin-right: 6px;
|
vben
authored
|
545
|
color: rgb(255 255 255 / 65%);
|
vben
authored
|
546
547
548
549
550
551
|
cursor: pointer;
&:hover {
color: #fff;
}
}
|
vben
authored
|
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;
|
vben
authored
|
580
581
582
583
|
top: 50px;
right: -1px;
width: 1px;
height: calc(100% - 50px);
|
vben
authored
|
584
|
cursor: ew-resize;
|
Vben
authored
|
585
|
background-color: #f8f8f9;
|
vben
authored
|
586
587
|
border-top: none;
border-bottom: none;
|
vben
authored
|
588
|
box-shadow: 0 0 4px 0 rgb(28 36 56 / 15%);
|
vben
authored
|
589
590
591
|
}
}
</style>
|