Files
vue3-element-admin/src/layouts/LeftLayout.vue
Ray.Hao fe3695d613 refactor(styles): 提取布局 mixin,派生主题色,消除硬编码与魔法数字
- 新增 _mixins.scss,统一 app-main / sidebar-scroll 高度计算
- _theme.scss 引入 SCSS 颜色令牌,menu-hover 改用 color-mix() 自动派生
- _variables.scss 新增 $sidebar-toggle-height 替代魔法数字 50px
- LeftLayout / TopLayout / MixLayout 中重复 calc 替换为 mixin 调用
- 修复 apifox / internal-doc demo 页面 tags-view 高度错误(34px→40px)
- 添加主题变量契约注释,方便后续新增主题
2026-06-09 14:31:40 +08:00

140 lines
2.9 KiB
Vue

<template>
<BaseLayout>
<!-- 左侧菜单 -->
<div
v-show="!appStore.contentFullscreen"
class="layout__sidebar"
:class="{ 'layout__sidebar--collapsed': !isSidebarOpen }"
>
<div :class="{ 'has-logo': showLogo }" class="layout-sidebar">
<LayoutLogo v-if="showLogo" :collapse="!isSidebarOpen" />
<el-scrollbar>
<LayoutSidebar :data="routes" base-path="" />
</el-scrollbar>
</div>
</div>
<!-- 主内容区 -->
<div
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 />
</div>
</BaseLayout>
</template>
<script setup lang="ts">
import { useLayout } from "./useLayout";
import { useAppStore } from "@/stores";
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 { showTagsView, showLogo, isSidebarOpen, routes } = useLayout();
const appStore = useAppStore();
</script>
<style lang="scss" scoped>
@use "@/styles/base/mixins" as *;
.layout {
&__sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 999;
width: $sidebar-width;
background-color: $menu-background;
transition: width 0.28s;
&--collapsed {
width: $sidebar-width-collapsed;
}
.layout-sidebar {
position: relative;
height: 100%;
background-color: var(--menu-background);
border-right: 1px solid var(--card-border);
transition: width 0.28s;
&.has-logo {
.el-scrollbar {
@include sidebar-scroll-height-with-logo;
}
}
:deep(.el-menu) {
border: none;
}
}
}
&__main {
position: relative;
height: 100%;
margin-left: $sidebar-width;
overflow-y: auto;
transition: margin-left 0.28s;
&--collapsed {
margin-left: $sidebar-width-collapsed;
}
&--fullscreen {
margin-left: 0 !important;
}
.fixed-header {
position: sticky;
top: 0;
z-index: 9;
transition: width 0.28s;
}
}
}
/* 移动端样式*/
.mobile {
.layout__sidebar {
width: $sidebar-width !important;
transition:
transform 0.28s,
width 0s;
}
&.hideSidebar {
.layout__sidebar {
transform: translateX(-$sidebar-width);
}
}
&.openSidebar {
.layout__sidebar {
transform: translateX(0);
}
}
.layout__main {
margin-left: 0 !important;
}
}
.hasTagsView {
:deep(.app-main) {
@include app-main-height-with-tags;
}
}
</style>