diff --git a/src/layout/index.vue b/src/layout/index.vue
deleted file mode 100644
index 9227afc8..00000000
--- a/src/layout/index.vue
+++ /dev/null
@@ -1,306 +0,0 @@
-
-
-
-
-
-
diff --git a/src/layouts/README.md b/src/layouts/README.md
new file mode 100644
index 00000000..f31eb7b5
--- /dev/null
+++ b/src/layouts/README.md
@@ -0,0 +1,59 @@
+# 布局系统
+
+本项目的布局系统采用模块化、可组合式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
+```
+
+## 主要功能
+
+1. **响应式适配**: 自动适配桌面端和移动端,移动端下自动收起侧边栏
+2. **多种布局模式**: 支持左侧菜单、顶部菜单、混合菜单三种模式
+3. **主题切换**: 支持明亮/暗黑主题
+4. **标签页**: 支持多标签页功能,可通过设置开启/关闭
+
+## 可组合式API
+
+### useLayout
+
+提供布局相关的基础功能:
+- 侧边栏展开/收起控制
+- 布局模式获取
+- 布局样式类计算
+
+### useLayoutResponsive
+
+提供响应式布局功能:
+- 根据屏幕尺寸自动调整设备类型
+- 根据设备类型自动调整侧边栏状态
+
+### useLayoutMenu
+
+提供菜单相关功能:
+- 获取菜单数据
+- 处理菜单激活状态
+- 混合布局下的菜单联动
\ No newline at end of file
diff --git a/src/layouts/components/LayoutBase.vue b/src/layouts/components/LayoutBase.vue
new file mode 100644
index 00000000..bcd43332
--- /dev/null
+++ b/src/layouts/components/LayoutBase.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/components/LayoutMenu.vue b/src/layouts/components/LayoutMenu.vue
new file mode 100644
index 00000000..2c5ae936
--- /dev/null
+++ b/src/layouts/components/LayoutMenu.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/components/LeftSideLayout/index.vue b/src/layouts/components/LeftSideLayout/index.vue
new file mode 100644
index 00000000..429f9eb6
--- /dev/null
+++ b/src/layouts/components/LeftSideLayout/index.vue
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/components/MixMenuLayout/index.vue b/src/layouts/components/MixMenuLayout/index.vue
new file mode 100644
index 00000000..aa78e9a2
--- /dev/null
+++ b/src/layouts/components/MixMenuLayout/index.vue
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/components/TopMenuLayout/index.vue b/src/layouts/components/TopMenuLayout/index.vue
new file mode 100644
index 00000000..9f7607c0
--- /dev/null
+++ b/src/layouts/components/TopMenuLayout/index.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/components/common/LayoutSidebar.vue b/src/layouts/components/common/LayoutSidebar.vue
new file mode 100644
index 00000000..6933bd30
--- /dev/null
+++ b/src/layouts/components/common/LayoutSidebar.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
diff --git a/src/layouts/composables/useLayout.ts b/src/layouts/composables/useLayout.ts
new file mode 100644
index 00000000..9fec35dd
--- /dev/null
+++ b/src/layouts/composables/useLayout.ts
@@ -0,0 +1,66 @@
+import { computed, watchEffect } from "vue";
+import { useAppStore, useSettingsStore } from "@/store";
+import defaultSettings from "@/settings";
+
+/**
+ * 布局相关的通用逻辑
+ */
+export function useLayout() {
+ const appStore = useAppStore();
+ const settingsStore = useSettingsStore();
+
+ // 计算当前布局模式
+ const currentLayout = computed(() => settingsStore.layout);
+
+ // 侧边栏展开状态
+ const isSidebarOpen = computed(() => appStore.sidebar.opened);
+
+ // 是否显示标签视图
+ const isShowTagsView = computed(() => settingsStore.tagsView);
+
+ // 是否显示设置面板
+ const isShowSettings = computed(() => defaultSettings.showSettings);
+
+ // 是否显示Logo
+ const isShowLogo = computed(() => settingsStore.sidebarLogo);
+
+ // 布局CSS类
+ const layoutClass = computed(() => ({
+ hideSidebar: !appStore.sidebar.opened,
+ openSidebar: appStore.sidebar.opened,
+ mobile: appStore.device === "mobile",
+ [`layout-${settingsStore.layout}`]: true,
+ }));
+
+ /**
+ * 处理切换侧边栏的展开/收起状态
+ */
+ function toggleSidebar() {
+ appStore.toggleSidebar();
+ }
+
+ /**
+ * 关闭侧边栏(移动端)
+ */
+ function closeSidebar() {
+ appStore.closeSideBar();
+ }
+
+ // 监听路由变化,在移动端自动关闭侧边栏
+ watchEffect(() => {
+ if (appStore.device === "mobile" && appStore.sidebar.opened) {
+ appStore.closeSideBar();
+ }
+ });
+
+ return {
+ currentLayout,
+ isSidebarOpen,
+ isShowTagsView,
+ isShowSettings,
+ isShowLogo,
+ layoutClass,
+ toggleSidebar,
+ closeSidebar,
+ };
+}
diff --git a/src/layouts/composables/useLayoutMenu.ts b/src/layouts/composables/useLayoutMenu.ts
new file mode 100644
index 00000000..68d35e9a
--- /dev/null
+++ b/src/layouts/composables/useLayoutMenu.ts
@@ -0,0 +1,58 @@
+import { computed, watch } from "vue";
+import { useRoute } from "vue-router";
+import { useAppStore, usePermissionStore } from "@/store";
+
+/**
+ * 布局菜单处理逻辑
+ */
+export function useLayoutMenu() {
+ const route = useRoute();
+ const appStore = useAppStore();
+ const permissionStore = usePermissionStore();
+
+ // 顶部菜单激活路径
+ const activeTopMenuPath = computed(() => appStore.activeTopMenuPath);
+
+ // 常规路由(左侧菜单或顶部菜单)
+ const routes = computed(() => permissionStore.routes);
+
+ // 混合布局左侧菜单路由
+ const sideMenuRoutes = computed(() => permissionStore.sideMenuRoutes);
+
+ // 当前激活的菜单
+ const activeMenu = computed(() => {
+ const { meta, path } = route;
+
+ // 如果设置了activeMenu,则使用
+ if (meta?.activeMenu) {
+ return meta.activeMenu;
+ }
+
+ return path;
+ });
+
+ // 监听顶部菜单路径变化,更新侧边菜单
+ watch(
+ () => activeTopMenuPath.value,
+ (newPath) => {
+ permissionStore.updateSideMenu(newPath);
+ },
+ { immediate: true }
+ );
+
+ /**
+ * 处理顶部菜单点击
+ * @param path 菜单路径
+ */
+ function handleTopMenuClick(path: string) {
+ appStore.activeTopMenu(path);
+ }
+
+ return {
+ routes,
+ sideMenuRoutes,
+ activeMenu,
+ activeTopMenuPath,
+ handleTopMenuClick,
+ };
+}
diff --git a/src/layouts/composables/useLayoutResponsive.ts b/src/layouts/composables/useLayoutResponsive.ts
new file mode 100644
index 00000000..b8dd9ada
--- /dev/null
+++ b/src/layouts/composables/useLayoutResponsive.ts
@@ -0,0 +1,36 @@
+import { watchEffect, computed } from "vue";
+import { useWindowSize } from "@vueuse/core";
+import { useAppStore } from "@/store";
+import { DeviceEnum } from "@/enums/settings/device.enum";
+
+/**
+ * 布局响应式处理逻辑
+ */
+export function useLayoutResponsive() {
+ const appStore = useAppStore();
+ const { width } = useWindowSize();
+
+ // 定义响应式断点
+ const WIDTH_DESKTOP = 992; // 桌面设备断点 (>=992px)
+
+ // 设置当前设备类型并调整侧边栏状态
+ watchEffect(() => {
+ const isDesktop = width.value >= WIDTH_DESKTOP;
+ const deviceType = isDesktop ? DeviceEnum.DESKTOP : DeviceEnum.MOBILE;
+
+ // 更新设备类型
+ appStore.toggleDevice(deviceType);
+
+ // 根据设备类型调整侧边栏状态
+ if (isDesktop) {
+ appStore.openSideBar();
+ } else {
+ appStore.closeSideBar();
+ }
+ });
+
+ return {
+ isDesktop: computed(() => width.value >= WIDTH_DESKTOP),
+ isMobile: computed(() => appStore.device === DeviceEnum.MOBILE),
+ };
+}
diff --git a/src/layouts/index.vue b/src/layouts/index.vue
new file mode 100644
index 00000000..6c7209a3
--- /dev/null
+++ b/src/layouts/index.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/src/router/index.ts b/src/router/index.ts
index 57058ee4..38bf3e60 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,7 +1,7 @@
import type { App } from "vue";
import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router";
-export const Layout = () => import("@/layout/index.vue");
+export const Layout = () => import("@/layouts/index.vue");
// 静态路由
export const constantRoutes: RouteRecordRaw[] = [
diff --git a/src/store/modules/permission.store.ts b/src/store/modules/permission.store.ts
index 968eb08a..007e786e 100644
--- a/src/store/modules/permission.store.ts
+++ b/src/store/modules/permission.store.ts
@@ -5,7 +5,7 @@ import router from "@/router";
import MenuAPI, { type RouteVO } from "@/api/system/menu.api";
const modules = import.meta.glob("../../views/**/**.vue");
-const Layout = () => import("@/layout/index.vue");
+const Layout = () => import("@/layouts/index.vue");
export const usePermissionStore = defineStore("permission", () => {
// 存储所有路由,包括静态路由和动态路由