wip: 布局优化完成
This commit is contained in:
@@ -1,96 +0,0 @@
|
||||
# 布局系统
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
layouts/
|
||||
├── index.vue # 布局系统入口,根据设置动态加载布局
|
||||
├── BaseLayout.vue # 基础布局容器,提供通用功能
|
||||
├── LeftLayout.vue # 左侧菜单布局
|
||||
├── TopLayout.vue # 顶部菜单布局
|
||||
├── MixLayout.vue # 混合布局(顶部+左侧)
|
||||
├── components/ # 布局相关组件
|
||||
│ ├── AppMain.vue # 主内容区域
|
||||
│ ├── NavBar.vue # 导航栏
|
||||
│ ├── NavbarActions.vue # 导航栏右侧操作区
|
||||
│ ├── TagsView.vue # 标签页视图
|
||||
│ ├── LayoutMenu.vue # 菜单组件
|
||||
│ ├── Sidebar/ # 侧边栏相关组件
|
||||
│ │ ├── SidebarLogo.vue # Logo 组件
|
||||
│ │ ├── SidebarMenu.vue # 菜单主体(未使用)
|
||||
│ │ ├── SidebarMenuItem.vue # 菜单项
|
||||
│ │ ├── SidebarMenuItemTitle.vue # 菜单项标题
|
||||
│ │ └── SidebarMixTopMenu.vue # 混合布局顶部菜单
|
||||
│ ├── Settings/ # 设置面板
|
||||
│ │ └── index.vue # 设置面板主组件(包含布局选择)
|
||||
│ └── common/ # 通用组件
|
||||
│ └── LayoutSidebar.vue # 侧边栏容器
|
||||
└── composables/ # 组合式函数
|
||||
├── useLayout.ts # 布局相关逻辑
|
||||
├── useLayoutMenu.ts # 菜单相关逻辑
|
||||
└── useLayoutResponsive.ts # 响应式处理
|
||||
```
|
||||
|
||||
## 布局说明
|
||||
|
||||
### 1. LeftLayout(左侧布局)
|
||||
- 传统的左侧固定菜单布局
|
||||
- 支持菜单折叠/展开
|
||||
- 适合大多数管理系统
|
||||
|
||||
### 2. TopLayout(顶部布局)
|
||||
- 菜单位于顶部横向排列
|
||||
- 适合一级菜单较少的系统
|
||||
- 节省横向空间
|
||||
|
||||
### 3. MixLayout(混合布局)
|
||||
- 一级菜单在顶部,二级菜单在左侧
|
||||
- 适合菜单层级较多的大型系统
|
||||
- 提供更好的菜单组织方式
|
||||
|
||||
## 使用方式
|
||||
|
||||
布局系统会根据 `settings store` 中的 `layout` 配置自动切换:
|
||||
|
||||
```typescript
|
||||
// 在设置面板中切换布局
|
||||
// 或通过代码:
|
||||
settingsStore.layout = LayoutMode.LEFT; // 'left' | 'top' | 'mix'
|
||||
```
|
||||
|
||||
## 自定义布局
|
||||
|
||||
如需添加新布局:
|
||||
|
||||
1. 在 `layouts/` 目录下创建新的布局组件(如 `CustomLayout.vue`)
|
||||
2. 在 `index.vue` 中导入并添加到切换逻辑
|
||||
3. 在 `enums/settings/layout.enum.ts` 中添加新的布局类型
|
||||
|
||||
## 主要功能
|
||||
|
||||
1. **响应式适配**: 自动适配桌面端和移动端,移动端下自动收起侧边栏
|
||||
2. **多种布局模式**: 支持左侧菜单、顶部菜单、混合菜单三种模式
|
||||
3. **主题切换**: 支持明亮/暗黑主题
|
||||
4. **标签页**: 支持多标签页功能,可通过设置开启/关闭
|
||||
|
||||
## 可组合式API
|
||||
|
||||
### useLayout
|
||||
|
||||
提供布局相关的基础功能:
|
||||
- 侧边栏展开/收起控制
|
||||
- 布局模式获取
|
||||
- 布局样式类计算
|
||||
|
||||
### useLayoutResponsive
|
||||
|
||||
提供响应式布局功能:
|
||||
- 根据屏幕尺寸自动调整设备类型
|
||||
- 根据设备类型自动调整侧边栏状态
|
||||
|
||||
### useLayoutMenu
|
||||
|
||||
提供菜单相关功能:
|
||||
- 获取菜单数据
|
||||
- 处理菜单激活状态
|
||||
- 混合布局下的菜单联动
|
||||
@@ -138,15 +138,44 @@ function logout() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 40px;
|
||||
height: $navbar-height;
|
||||
min-width: 36px;
|
||||
height: 100%;
|
||||
padding: 0 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
// 确保直接子元素也居中
|
||||
> * {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 确保 el-dropdown 也居中
|
||||
:deep(.el-dropdown) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 修复可能的 SVG 图标容器
|
||||
:deep(.icon-container),
|
||||
:deep(.action-icon) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 默认图标样式(明亮模式 + 左侧布局)
|
||||
:deep([class^="i-svg:"]) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
color: var(--el-text-color-regular);
|
||||
transition: color 0.3s;
|
||||
}
|
||||
@@ -165,18 +194,20 @@ function logout() {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 0 13px;
|
||||
padding: 0 8px;
|
||||
|
||||
&__avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__name {
|
||||
margin-left: 10px;
|
||||
margin-left: 8px;
|
||||
color: var(--el-text-color-regular);
|
||||
transition: color 0.3s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,16 @@ function toggleSideBar() {
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: $navbar-height;
|
||||
padding: 0 20px;
|
||||
background: var(--el-bg-color);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,7 @@ defineProps({
|
||||
background-color: $sidebar-logo-background;
|
||||
|
||||
.title {
|
||||
flex-shrink: 0; /* 防止容器在空间不足时缩小 */
|
||||
flex-shrink: 0;
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useLayout } from "../composables/useLayout";
|
||||
import { useLayoutResponsive } from "../composables/useLayoutResponsive";
|
||||
import Settings from "../components/Settings.vue";
|
||||
import Settings from "../components/Settings/index.vue";
|
||||
|
||||
// 布局相关
|
||||
const { layoutClass, isShowSettings, isSidebarOpen, closeSidebar } = useLayout();
|
||||
|
||||
@@ -29,10 +29,10 @@ import { useLayout } from "../composables/useLayout";
|
||||
import { useLayoutMenu } from "../composables/useLayoutMenu";
|
||||
import BaseLayout from "./BaseLayout.vue";
|
||||
import Sidebar from "../components/Sidebar/index.vue";
|
||||
import NavBar from "../components/navbar/index.vue";
|
||||
import TagsView from "../components/TagsView.vue";
|
||||
import AppMain from "../components/AppMain.vue";
|
||||
import Menu from "../components/menu/index.vue";
|
||||
import NavBar from "../components/Navbar/index.vue";
|
||||
import TagsView from "../components/TagsView/index.vue";
|
||||
import AppMain from "../components/AppMain/index.vue";
|
||||
import Menu from "../components/Menu/index.vue";
|
||||
|
||||
// 布局相关参数
|
||||
const { isShowTagsView, isShowLogo, isSidebarOpen, isMobile } = useLayout();
|
||||
|
||||
@@ -64,11 +64,11 @@ import { useLayout } from "../composables/useLayout";
|
||||
import { useLayoutMenu } from "../composables/useLayoutMenu";
|
||||
import BaseLayout from "./BaseLayout.vue";
|
||||
import SidebarLogo from "../components/Sidebar/components/SidebarLogo.vue";
|
||||
import MixTopMenu from "../components/menu/components/MixTopMenu.vue";
|
||||
import NavbarActions from "../components/navbar/components/NavbarActions.vue";
|
||||
import TagsView from "../components/TagsView.vue";
|
||||
import AppMain from "../components/AppMain.vue";
|
||||
import MenuItem from "../components/menu/components/MenuItem.vue";
|
||||
import MixTopMenu from "../components/Menu/components/MixTopMenu.vue";
|
||||
import NavbarActions from "../components/Navbar/components/NavbarActions.vue";
|
||||
import TagsView from "../components/TagsView/index.vue";
|
||||
import AppMain from "../components/AppMain/index.vue";
|
||||
import MenuItem from "../components/Menu/components/MenuItem.vue";
|
||||
import Hamburger from "@/components/Hamburger/index.vue";
|
||||
import variables from "@/styles/variables.module.scss";
|
||||
|
||||
|
||||
@@ -2,10 +2,16 @@
|
||||
<BaseLayout>
|
||||
<!-- 顶部菜单栏 -->
|
||||
<div class="layout__header">
|
||||
<Sidebar :show-logo="isShowLogo" :is-collapsed="false">
|
||||
<div class="layout__header-left">
|
||||
<!-- Logo -->
|
||||
<SidebarLogo v-if="isShowLogo" :collapse="false" />
|
||||
<!-- 菜单 -->
|
||||
<Menu :data="routes" menu-mode="horizontal" base-path="" />
|
||||
</div>
|
||||
<!-- 操作按钮 -->
|
||||
<div class="layout__header-right">
|
||||
<NavbarActions />
|
||||
</Sidebar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
@@ -20,11 +26,11 @@
|
||||
import { useLayout } from "../composables/useLayout";
|
||||
import { useLayoutMenu } from "../composables/useLayoutMenu";
|
||||
import BaseLayout from "./BaseLayout.vue";
|
||||
import Sidebar from "../components/Sidebar/index.vue";
|
||||
import Menu from "../components/menu/index.vue";
|
||||
import NavbarActions from "../components/navbar/components/NavbarActions.vue";
|
||||
import TagsView from "../components/TagsView.vue";
|
||||
import AppMain from "../components/AppMain.vue";
|
||||
import SidebarLogo from "../components/Sidebar/components/SidebarLogo.vue";
|
||||
import Menu from "../components/Menu/index.vue";
|
||||
import NavbarActions from "../components/Navbar/components/NavbarActions.vue";
|
||||
import TagsView from "../components/TagsView/index.vue";
|
||||
import AppMain from "../components/AppMain/index.vue";
|
||||
|
||||
// 布局相关参数
|
||||
const { isShowTagsView, isShowLogo } = useLayout();
|
||||
@@ -39,14 +45,72 @@ const { routes } = useLayoutMenu();
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: $navbar-height;
|
||||
padding: 0 20px;
|
||||
background-color: $menu-background;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
|
||||
:deep(.layout-sidebar) {
|
||||
&-left {
|
||||
display: flex;
|
||||
width: 100% !important;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
|
||||
// Logo 样式
|
||||
:deep(.sidebar-logo) {
|
||||
width: 200px;
|
||||
height: $navbar-height;
|
||||
padding: 0;
|
||||
margin-right: 20px;
|
||||
background: transparent;
|
||||
|
||||
.sidebar-logo__link {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 限制菜单高度
|
||||
:deep(.el-menu--horizontal) {
|
||||
flex: 1;
|
||||
height: $navbar-height;
|
||||
line-height: $navbar-height;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
.el-menu-item {
|
||||
height: $navbar-height;
|
||||
line-height: $navbar-height;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.el-sub-menu {
|
||||
.el-sub-menu__title {
|
||||
height: $navbar-height;
|
||||
line-height: $navbar-height;
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// 修复子菜单弹出位置
|
||||
.el-menu--popup {
|
||||
min-width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
11
src/types/components.d.ts
vendored
11
src/types/components.d.ts
vendored
@@ -7,7 +7,6 @@ export {}
|
||||
declare module "vue" {
|
||||
export interface GlobalComponents {
|
||||
AppLink: (typeof import("./../components/AppLink/index.vue"))["default"];
|
||||
AppMain: (typeof import("./../layout/components/AppMain/index.vue"))["default"];
|
||||
Breadcrumb: (typeof import("./../components/Breadcrumb/index.vue"))["default"];
|
||||
CopyButton: (typeof import("./../components/CopyButton/index.vue"))["default"];
|
||||
CURD: (typeof import("./../components/CURD/index.vue"))["default"];
|
||||
@@ -71,10 +70,8 @@ declare module "vue" {
|
||||
Hamburger: (typeof import("./../components/Hamburger/index.vue"))["default"];
|
||||
IconSelect: (typeof import("./../components/IconSelect/index.vue"))["default"];
|
||||
LangSelect: (typeof import("./../components/LangSelect/index.vue"))["default"];
|
||||
LayoutSelect: (typeof import("./../layout/components/Settings/components/LayoutSelect.vue"))["default"];
|
||||
MenuSearch: (typeof import("./../components/MenuSearch/index.vue"))["default"];
|
||||
MultiImageUpload: (typeof import("./../components/Upload/MultiImageUpload.vue"))["default"];
|
||||
NavbarActions: (typeof import("./../layout/components/NavBar/components/NavbarActions.vue"))["default"];
|
||||
Notification: (typeof import("./../components/Notification/index.vue"))["default"];
|
||||
PageContent: (typeof import("./../components/CURD/PageContent.vue"))["default"];
|
||||
PageModal: (typeof import("./../components/CURD/PageModal.vue"))["default"];
|
||||
@@ -82,17 +79,9 @@ declare module "vue" {
|
||||
Pagination: (typeof import("./../components/Pagination/index.vue"))["default"];
|
||||
RouterLink: (typeof import("vue-router"))["RouterLink"];
|
||||
RouterView: (typeof import("vue-router"))["RouterView"];
|
||||
Settings: (typeof import("./../layout/components/Settings/index.vue"))["default"];
|
||||
Sidebar: (typeof import("./../layout/components/Sidebar/index.vue"))["default"];
|
||||
SidebarLogo: (typeof import("./../layout/components/Sidebar/components/SidebarLogo.vue"))["default"];
|
||||
SidebarMenu: (typeof import("./../layout/components/Sidebar/components/SidebarMenu.vue"))["default"];
|
||||
SidebarMenuItem: (typeof import("./../layout/components/Sidebar/components/SidebarMenuItem.vue"))["default"];
|
||||
SidebarMenuItemTitle: (typeof import("./../layout/components/Sidebar/components/SidebarMenuItemTitle.vue"))["default"];
|
||||
SidebarMixTopMenu: (typeof import("./../layout/components/Sidebar/components/SidebarMixTopMenu.vue"))["default"];
|
||||
SingleImageUpload: (typeof import("./../components/Upload/SingleImageUpload.vue"))["default"];
|
||||
SizeSelect: (typeof import("./../components/SizeSelect/index.vue"))["default"];
|
||||
TableSelect: (typeof import("./../components/TableSelect/index.vue"))["default"];
|
||||
TagsView: (typeof import("./../layout/components/TagsView/index.vue"))["default"];
|
||||
WangEditor: (typeof import("./../components/WangEditor/index.vue"))["default"];
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
|
||||
Reference in New Issue
Block a user