diff --git a/src/layouts/README.md b/src/layouts/README.md index f31eb7b5..9d36e764 100644 --- a/src/layouts/README.md +++ b/src/layouts/README.md @@ -1,34 +1,71 @@ # 布局系统 -本项目的布局系统采用模块化、可组合式API的架构,支持三种不同的布局模式: - -1. **左侧菜单布局 (LeftSideLayout)**: 传统的管理系统布局,左侧为菜单栏,顶部为导航栏 -2. **顶部菜单布局 (TopMenuLayout)**: 顶部为主菜单栏,适合菜单项较少的应用 -3. **混合菜单布局 (MixMenuLayout)**: 顶部为一级菜单,左侧为对应的子菜单,适合菜单层级较多的复杂应用 - ## 目录结构 ``` layouts/ -├── README.md # 文档说明 -├── index.vue # 布局入口,根据设置选择对应的布局组件 -├── composables/ # 可组合式API -│ ├── useLayout.ts # 布局通用逻辑 -│ ├── useLayoutResponsive.ts # 响应式布局逻辑 -│ └── useLayoutMenu.ts # 菜单处理逻辑 -└── components/ # 布局组件 - ├── LayoutBase.vue # 基础布局组件 - ├── SidebarMenu.vue # 菜单组件 - ├── common/ # 公共组件 - │ └── LayoutSidebar.vue # 侧边栏公共组件 - ├── LeftSideLayout/ # 左侧菜单布局 - │ └── index.vue - ├── TopMenuLayout/ # 顶部菜单布局 - │ └── index.vue - └── MixMenuLayout/ # 混合菜单布局 - └── index.vue +├── 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. **响应式适配**: 自动适配桌面端和移动端,移动端下自动收起侧边栏 diff --git a/src/layouts/components/AppMain/index.vue b/src/layouts/components/AppMain.vue similarity index 100% rename from src/layouts/components/AppMain/index.vue rename to src/layouts/components/AppMain.vue diff --git a/src/layouts/components/Settings/index.vue b/src/layouts/components/Settings.vue similarity index 63% rename from src/layouts/components/Settings/index.vue rename to src/layouts/components/Settings.vue index 06554be0..925e04ac 100644 --- a/src/layouts/components/Settings/index.vue +++ b/src/layouts/components/Settings.vue @@ -61,7 +61,31 @@
{{ t("settings.navigation") }} - + + +
+ +
+
+
+
+ +
@@ -72,6 +96,20 @@ import { LayoutMode } from "@/enums/settings/layout.enum"; import { ThemeMode } from "@/enums/settings/theme.enum"; import { SidebarColor } from "@/enums/settings/theme.enum"; import { useSettingsStore, usePermissionStore, useAppStore } from "@/store"; + +// 布局选项配置 +interface LayoutOption { + value: LayoutMode; + label: string; + className: string; +} + +const layoutOptions: LayoutOption[] = [ + { value: LayoutMode.LEFT, label: "左侧模式", className: "left" }, + { value: LayoutMode.TOP, label: "顶部模式", className: "top" }, + { value: LayoutMode.MIX, label: "混合模式", className: "mix" }, +]; + // 颜色预设 const colorPresets = [ "#4080FF", @@ -190,4 +228,97 @@ const handleCloseDrawer = () => { } } } + +/* 布局选择器样式 */ +.layout-select { + display: flex; + gap: 10px; + justify-content: space-evenly; + padding: 10px 0; + --layout-primary: #1b2a47; + --layout-background: #f0f2f5; + --layout-shadow: 0 0 8px rgba(0, 0, 0, 0.1); + --layout-hover: #e3f1f9; +} + +.layout-item { + position: relative; + width: 18%; + height: 50px; + cursor: pointer; + background: var(--layout-background); + border-radius: 8px; + box-shadow: var(--layout-shadow); + + transition: + transform 0.2s ease, + border-color 0.2s ease, + box-shadow 0.2s ease; + + &:hover { + background-color: var(--layout-hover); + transform: scale(1.02); + } + + &:focus-visible { + outline: 2px solid var(--el-color-primary); + } + + &-part { + position: absolute; + background: var(--layout-primary); + border-radius: 4px; + box-shadow: var(--layout-shadow); + transition: all 0.3s ease; + } + + &.left { + .layout-item-part { + &:first-child { + width: 30%; + height: 100%; + border-radius: 4px 0 0 4px; + } + &:last-child { + top: 0; + right: 0; + width: 70%; + height: 30%; + background: #fff; + border-radius: 0 4px 4px 0; + } + } + } + + &.top { + .layout-item-part:first-child { + width: 100%; + height: 30%; + border-radius: 4px 4px 0 0; + } + } + + &.mix { + .layout-item-part { + &:first-child { + width: 100%; + height: 30%; + border-radius: 4px 4px 0 0; + } + &:last-child { + bottom: 0; + left: 0; + width: 30%; + height: 70%; + border-radius: 0 0 4px 4px; + } + } + } +} + +.is-active { + background-color: var(--layout-hover); + border: 2px solid var(--el-color-primary); + transform: scale(1.05); +} diff --git a/src/layouts/components/Settings/components/LayoutSelect.vue b/src/layouts/components/Settings/components/LayoutSelect.vue deleted file mode 100644 index 488341cc..00000000 --- a/src/layouts/components/Settings/components/LayoutSelect.vue +++ /dev/null @@ -1,140 +0,0 @@ - - - - - diff --git a/src/layouts/components/Sidebar/components/SidebarMenu.vue b/src/layouts/components/Sidebar/components/SidebarMenu.vue deleted file mode 100644 index 4c8b36f5..00000000 --- a/src/layouts/components/Sidebar/components/SidebarMenu.vue +++ /dev/null @@ -1,132 +0,0 @@ - - - - diff --git a/src/layouts/components/common/LayoutSidebar.vue b/src/layouts/components/Sidebar/index.vue similarity index 92% rename from src/layouts/components/common/LayoutSidebar.vue rename to src/layouts/components/Sidebar/index.vue index 6933bd30..a022aeef 100644 --- a/src/layouts/components/common/LayoutSidebar.vue +++ b/src/layouts/components/Sidebar/index.vue @@ -8,6 +8,8 @@