339 lines
7.9 KiB
Vue
339 lines
7.9 KiB
Vue
<template>
|
|
<BaseLayout>
|
|
<aside
|
|
v-show="!appStore.contentFullscreen"
|
|
class="layout__double-sidebar"
|
|
:class="{ 'layout__double-sidebar--collapsed': !isSidebarOpen }"
|
|
>
|
|
<div class="layout__double-primary">
|
|
<LayoutLogo v-if="showLogo" :collapse="true" />
|
|
|
|
<el-scrollbar class="layout__double-primary-scroll">
|
|
<button
|
|
v-for="item in topMenuItems"
|
|
:key="item.path"
|
|
class="layout__primary-item"
|
|
:class="{ 'is-active': item.path === activeTopMenuPath }"
|
|
:title="item.meta?.title ? translateRouteTitle(item.meta.title) : undefined"
|
|
type="button"
|
|
@click="handleTopMenuSelect(item.path)"
|
|
>
|
|
<MenuIcon :icon="item.meta?.icon" />
|
|
<span v-if="item.meta?.title">{{ translateRouteTitle(item.meta.title) }}</span>
|
|
</button>
|
|
</el-scrollbar>
|
|
|
|
<button
|
|
class="layout__primary-toggle"
|
|
:title="isSidebarOpen ? '收起菜单' : '展开菜单'"
|
|
type="button"
|
|
@click="toggleSidebar"
|
|
>
|
|
<div class="i-svg:arrow-left-right layout__primary-toggle-icon" />
|
|
</button>
|
|
</div>
|
|
|
|
<div class="layout__double-secondary">
|
|
<div v-if="showLogo" class="layout__double-title">
|
|
{{ appConfig.title }}
|
|
</div>
|
|
|
|
<el-scrollbar class="layout__double-secondary-scroll">
|
|
<LayoutSidebar
|
|
:data="sideMenuRoutes"
|
|
:base-path="activeTopMenuPath"
|
|
:always-expand="true"
|
|
/>
|
|
</el-scrollbar>
|
|
</div>
|
|
</aside>
|
|
|
|
<main
|
|
class="layout__main"
|
|
:class="{
|
|
hasTagsView: showTagsView,
|
|
'layout__main--collapsed': !isSidebarOpen,
|
|
'layout__main--fullscreen': appStore.contentFullscreen,
|
|
}"
|
|
>
|
|
<LayoutNavbar v-show="!appStore.contentFullscreen" />
|
|
<LayoutTagsView v-if="showTagsView" />
|
|
<LayoutMain />
|
|
</main>
|
|
</BaseLayout>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useLayout } from "./useLayout";
|
|
import { useMixMenu } from "./composables/useMixMenu";
|
|
import { useAppStore } from "@/stores";
|
|
import { appConfig } from "@/settings";
|
|
import { translateRouteTitle } from "@/lang/utils";
|
|
import BaseLayout from "./BaseLayout.vue";
|
|
import LayoutLogo from "./components/LayoutLogo.vue";
|
|
import LayoutNavbar from "./components/LayoutNavbar.vue";
|
|
import LayoutTagsView from "./components/LayoutTagsView.vue";
|
|
import LayoutMain from "./components/LayoutMain.vue";
|
|
import LayoutSidebar from "./components/LayoutSidebar.vue";
|
|
|
|
const appStore = useAppStore();
|
|
const { showTagsView, showLogo, isSidebarOpen, toggleSidebar } = useLayout();
|
|
|
|
const { MenuIcon, topMenuItems, activeTopMenuPath, sideMenuRoutes, handleTopMenuSelect } =
|
|
useMixMenu();
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@use "@/styles/mixins" as *;
|
|
|
|
$double-sidebar-width: $sidebar-primary-width + $sidebar-secondary-width;
|
|
|
|
.layout {
|
|
&__double-sidebar {
|
|
position: fixed;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
z-index: 999;
|
|
display: flex;
|
|
width: $double-sidebar-width;
|
|
overflow: hidden;
|
|
background-color: var(--menu-background);
|
|
border-right: 1px solid var(--menu-border);
|
|
transition: width 0.28s;
|
|
|
|
&--collapsed {
|
|
width: $sidebar-width-collapsed + $sidebar-secondary-width;
|
|
|
|
.layout__double-primary {
|
|
flex-basis: $sidebar-width-collapsed;
|
|
width: $sidebar-width-collapsed;
|
|
}
|
|
|
|
.layout__primary-item {
|
|
gap: 0;
|
|
min-height: 48px;
|
|
padding: 8px 4px;
|
|
|
|
span {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.layout__double-secondary {
|
|
flex: 0 0 $sidebar-secondary-width;
|
|
width: $sidebar-secondary-width;
|
|
min-width: $sidebar-secondary-width;
|
|
border-right: 1px solid var(--menu-border);
|
|
}
|
|
}
|
|
}
|
|
|
|
&__double-primary {
|
|
display: flex;
|
|
flex: 0 0 $sidebar-primary-width;
|
|
flex-direction: column;
|
|
width: $sidebar-primary-width;
|
|
overflow: hidden;
|
|
background:
|
|
linear-gradient(
|
|
180deg,
|
|
color-mix(in srgb, var(--menu-background) 94%, var(--el-color-primary) 6%),
|
|
var(--menu-background)
|
|
),
|
|
var(--menu-background);
|
|
border-right: 1px solid var(--menu-border);
|
|
transition:
|
|
flex-basis 0.28s,
|
|
width 0.28s;
|
|
|
|
:deep(.logo) {
|
|
height: $navbar-height;
|
|
background-color: transparent;
|
|
}
|
|
}
|
|
|
|
&__double-primary-scroll {
|
|
flex: 1;
|
|
min-height: 0;
|
|
}
|
|
|
|
&__primary-item {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: calc(100% - 12px);
|
|
min-height: 64px;
|
|
padding: 8px 4px;
|
|
margin: 6px;
|
|
font: inherit;
|
|
font-size: 12px;
|
|
line-height: 1.2;
|
|
color: var(--menu-text);
|
|
cursor: pointer;
|
|
background: transparent;
|
|
border: 0;
|
|
border-radius: 8px;
|
|
transition:
|
|
color 0.18s,
|
|
background-color 0.18s;
|
|
|
|
:deep(.el-icon),
|
|
:deep([class^="i-svg:"]) {
|
|
width: 20px;
|
|
height: 20px;
|
|
font-size: 20px;
|
|
color: currentcolor !important;
|
|
}
|
|
|
|
span {
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
&:hover {
|
|
color: var(--menu-active-text);
|
|
background-color: var(--menu-hover);
|
|
}
|
|
|
|
&.is-active {
|
|
color: var(--menu-active-text);
|
|
background-color: var(--menu-active-bg);
|
|
}
|
|
|
|
&:focus-visible {
|
|
outline: none;
|
|
background-color: var(--menu-hover);
|
|
}
|
|
}
|
|
|
|
&__primary-toggle {
|
|
display: flex;
|
|
flex: 0 0 48px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
padding: 0;
|
|
font: inherit;
|
|
color: var(--menu-text);
|
|
cursor: pointer;
|
|
background: transparent;
|
|
border: none;
|
|
border-top: 1px solid var(--menu-border);
|
|
transition:
|
|
color 0.18s,
|
|
background-color 0.18s;
|
|
|
|
&:hover {
|
|
color: var(--menu-active-text);
|
|
background-color: var(--menu-hover);
|
|
}
|
|
}
|
|
|
|
&__primary-toggle-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
font-size: 18px;
|
|
transition: transform 0.28s;
|
|
}
|
|
|
|
&__double-secondary {
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
flex-direction: column;
|
|
width: $sidebar-secondary-width;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
background-color: var(--menu-background);
|
|
transition: width 0.28s;
|
|
|
|
:deep(.el-menu) {
|
|
border: none;
|
|
}
|
|
|
|
/* 恢复 .hideSidebar 被隐藏的文字(第二列始终展开) */
|
|
:deep(.el-menu-item span),
|
|
:deep(.el-sub-menu__title span) {
|
|
visibility: visible !important;
|
|
width: auto !important;
|
|
height: auto !important;
|
|
overflow: visible !important;
|
|
}
|
|
|
|
:deep(.el-sub-menu__icon-arrow) {
|
|
display: inline-flex !important;
|
|
}
|
|
}
|
|
|
|
&__double-secondary-scroll {
|
|
flex: 1;
|
|
min-height: 0;
|
|
}
|
|
|
|
&__double-title {
|
|
display: flex;
|
|
flex: 0 0 $navbar-height;
|
|
align-items: center;
|
|
min-width: $sidebar-secondary-width;
|
|
padding: 0 18px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--sidebar-logo-text-color);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
&__main {
|
|
position: relative;
|
|
height: 100%;
|
|
margin-left: $double-sidebar-width;
|
|
overflow-y: auto;
|
|
transition: margin-left 0.28s;
|
|
|
|
&--collapsed {
|
|
margin-left: $sidebar-width-collapsed + $sidebar-secondary-width;
|
|
}
|
|
|
|
&--fullscreen {
|
|
margin-left: 0 !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
.hasTagsView {
|
|
:deep(.app-main) {
|
|
@include app-main-height-with-tags;
|
|
}
|
|
}
|
|
|
|
.mobile {
|
|
.layout__double-sidebar {
|
|
width: $double-sidebar-width;
|
|
transition:
|
|
transform 0.28s,
|
|
width 0s;
|
|
}
|
|
|
|
&.hideSidebar {
|
|
.layout__double-sidebar {
|
|
transform: translateX(-$double-sidebar-width);
|
|
}
|
|
}
|
|
|
|
&.openSidebar {
|
|
.layout__double-sidebar {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
.layout__main {
|
|
margin-left: 0 !important;
|
|
}
|
|
}
|
|
</style>
|