尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Vue3 + Element Plus 后台管理系统首页布局实战:从骨架到动态菜单

发布时间:2026/9/30 1:13:10

资讯中心
01
ARTICLE

Vue3 + Element Plus 后台管理系统首页布局实战:从骨架到动态菜单

Vue3 + Element Plus 后台管理系统首页布局实战:从骨架到动态菜单
从零搭一套 Vue3 后台管理系统首页布局其实没那么玄乎。做后台管理系统一上来就是页面框架怎么搭左边菜单、顶部栏、中间内容区这套骨架定了后面塞页面就顺了。我这次用的是 Vue3 组合式 API 配 Element Plus从头把首页布局完整做了一遍包含侧边栏动态渲染、顶栏面包屑、内容区路由联动这些核心环节顺手把折叠、高亮、响应式这些细节也补齐了。这篇就把整个实现过程和一些踩坑点写清楚适合刚接触 Vue3 或者准备把老项目迁到 Vue3 Element Plus 的朋友参考。1. 项目整体设计与思路拆解1.1 技术栈选型为什么选 Vue3 Element Plus Vite后台管理系统是前端开发里最典型的固定场景表格、表单、弹窗、菜单导航90% 的页面都是这些组件的排列组合。选 Vue3 没什么悬念组合式 API 代码复用性更好script setup写起来整个人的精神状态都比 Options API 舒服一大截。Element Plus 则是国内中后台项目事实上的标准组件库除了组件种类全还有一个被很多人忽略的优势——中文文档和社区案例足够多遇到问题搜解决方案比用别的库容易得多。构建工具我选了 Vite而不是 Webpack。Vite 在开发环境启动是毫秒级HMR 跟手对大型后台系统来说体感差距非常大。但要注意的是 Vite 生产构建用的是 Rollup一些老依赖做搬迁时可能要在vite.config.ts里做额外兼容配置这个问题在后面我细说。1.2 首页布局结构拆解先定骨架再填血肉后台首页布局核心其实就是三块侧边栏el-aside、顶栏el-header、主内容区el-main。Element Plus 提供了el-container布局容器组件可以直接组合出经典后台结构el-container ├── el-aside侧边栏logo 菜单 └── el-container ├── el-header顶栏折叠按钮 面包屑 操作区 └── el-main内容区嵌套 router-view嵌套的el-container保证了侧边栏独立撑满整个页面高度顶栏和内容区在右侧区域纵向排列。这套结构基本是后台系统的最优解参考了业界大量成熟项目的验证不要自己随便魔改。布局结构一旦定了后续所有业务页面的开发都是往中间这个main区域填内容所以首页布局这个地基必须一次打好。1.3 目录结构组织先规划好免得后面返工项目初始化之前目录结构我建议直接按模块化划分。实际开发中经常遇到这种情况路由和菜单写在同一个文件里页面组件堆成一坨两个开发同时改一个文件天天冲突。所以我这次的目录是这样的src/ ├── api/ # 接口请求 │ └── modules/ ├── assets/ # 静态资源 ├── components/ # 全局通用组件 ├── hooks/ # 组合式函数 ├── layout/ # 布局组件本次核心 │ ├── components/ │ │ ├── Sidebar/ │ │ ├── Navbar/ │ │ └── Breadcrumb/ │ └── index.vue ├── router/ # 路由配置 │ └── modules/ ├── store/ # 状态管理 ├── styles/ # 全局样式变量、mixin └── views/ # 页面组件 └── dashboard/这样划分的核心思路是让每个模块职责单一化。布局组件layout/里只放与框架相关的壳子业务页面全部下沉到views/接口调用统一收敛到api/。实际多人协作时每个人改自己负责的文件夹冲突率大幅下降。2. 环境准备与项目脚手架搭建2.1 Vite 创建 Vue3 项目这一步要选对模板创建项目直接用官方脚手架开发环境下依赖 Node.js 版本建议不低于 18.0.0。实测在 Node 16 环境下跑 Vite 5 会报错所以先把环境升级好省得后面折腾半天。npm create vitelatest p9-admin -- --template vue-ts cd p9-admin npm install很多人喜欢在创建的时候选 JavaScript 而非 TypeScript但对于后台管理系统这种比业务复杂度只高不低的场景我建议直接 TS。菜单路由配置、接口返回数据这些如果连类型约束都没有项目一大维护成本直接爆炸。2.2 安装 Element Plus 与自动按需导入Element Plus 的引入方式有全局引入和按需引入。全局引入省事但打包体积大一个完整的后台项目最终 bundle 会多出几百 KB按需引入能让构建产物精简很多。按需引入使用官方推荐的unplugin-auto-import配合unplugin-vue-componentsnpm install element-plus npm install -D unplugin-auto-import unplugin-vue-components然后在vite.config.ts里配置两个插件import { defineConfig } from vite import vue from vitejs/plugin-vue import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })配置完成之后在组件模板里直接写el-button就能自动加载对应组件和样式ElMessage这类 API 风格的函数也能直接使用而不需要手动 import。不过有一个坑按需导入并不会自动导入全量的样式变量。如果你用到 Element Plus 的主题定制特别是覆盖 SCSS 变量比如修改主色AutoImport 方式会有样式覆盖不到的边界情况需要在src/styles/element/index.scss里单独处理。2.3 基础配置路由、状态管理、全局样式路由我用 Vue Router 4 的 createWebHistory 模式也就是 HTML5 History 模式。这里有生产环境部署的问题要提前想清楚History 模式需要 Nginx 配合做try_files回退到index.html如果部署环境不归你管、没法保证这个配置那就老实退回 Hash 模式。我在本地开发环境直接用 History生产部署时根据运维条件再切换。状态管理选择了 Pinia主要用来存用户信息、菜单权限、全局 UI 状态比如侧边栏折叠。Pinia 比 Vuex 轻量太多写起来也没有那么多冗余概念没有 mutation 这种工序直接改 state开发体验顺畅得多。// store/modules/app.ts import { defineStore } from pinia export const useAppStore defineStore(app, { state: () ({ sidebarCollapsed: false }), actions: { toggleSidebar() { this.sidebarCollapsed !this.sidebarCollapsed } } })全局样式这块我建议用 SCSS 加上 CSS 变量双管齐下。SCSS 变量在编译期生效适合做主题色切换这类定制CSS 变量在运行期可以动态修改适合做暗黑模式。后台项目后续大概率会遇到主题定制需求所以在第一步就把样式方案想好后面改起来不用伤筋动骨。3. 首页布局核心实现3.1 布局容器组件layout/index.vue 的搭建布局容器是整个后台的骨架在layout/index.vue里把结构搭好。核心是侧边栏、顶栏、内容区三个子组件通过状态管理里的sidebarCollapsed控制侧边栏折叠。为了固定整个后台的高度必须在根容器上设置height: 100vh同时给el-aside、el-header、el-main设置对应的高度和溢出规则。!-- layout/index.vue -- template el-container classapp-wrapper el-aside :widthappStore.sidebarCollapsed ? 64px : 220px classsidebar-container Sidebar / /el-aside el-container classmain-container el-header classnavbar height56px Navbar / /el-header el-main classapp-main router-view / /el-main /el-container /el-container /template script setup langts import Sidebar from ./components/Sidebar/index.vue import Navbar from ./components/Navbar/index.vue import { useAppStore } from /store/modules/app const appStore useAppStore() /script style scoped langscss .app-wrapper { height: 100vh; width: 100%; } .sidebar-container { transition: width 0.28s; background-color: #263445; overflow: hidden; } .navbar { background: #fff; box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); display: flex; align-items: center; } .app-main { background-color: #f0f2f5; padding: 16px; overflow-y: auto; } /style侧边栏的宽度过渡用了transition: width 0.28s这个动画时长是 Element 官方布局示例里常用的值实际体验比较顺滑。如果你觉得卡顿可以考虑把菜单里面的文字透明度也加上过渡折叠时文字淡出、展开时淡入交互感更好。侧边栏背景色用了一个深色系#263445Element 默认菜单在深色背景下的对比度表现尚可如果想更加精致可以使用 Element Plus 的el-menu的background-color、text-color、active-text-color属性做统一调整。3.2 侧边栏菜单从静态菜单到动态渲染后台系统的菜单可以分成两种静态写死的菜单和根据路由自动生成的动态菜单。一个合格的后台系统侧边栏菜单应该是基于路由配置自动生成的因为菜单高亮、面包屑导航、路由跳转需要共用同一条数据源分开维护迟早会出现不同步的问题。路由在router/modules下按业务划分模块每个模块是一个RouteRecordRaw数组的一部分。比如 Dashboard 模块// router/modules/dashboard.ts const routes [ { path: /dashboard, component: () import(/layout/index.vue), redirect: /dashboard/analysis, meta: { title: 首页, icon: HomeFilled }, children: [ { path: analysis, name: DashboardAnalysis, component: () import(/views/dashboard/analysis.vue), meta: { title: 分析页, icon: DataAnalysis } }, { path: monitor, name: DashboardMonitor, component: () import(/views/dashboard/monitor.vue), meta: { title: 监控页, icon: Monitor } } ] } ]侧边栏组件遍历路由表对meta.title做菜单标题渲染有子路由的自动生成el-sub-menu。核心代码如下!-- Sidebar/index.vue -- template div classsidebar-inner div classlogo span v-if!appStore.sidebarCollapsedP9 Admin/span span v-elseP9/span /div el-menu :default-activeroute.path :collapseappStore.sidebarCollapsed background-color#263445 text-color#bfcbd9 active-text-color#409EFF router unique-opened SidebarItem v-foritem in sidebarRoutes :keyitem.path :itemitem / /el-menu /div /template script setup langts import { computed } from vue import { useRoute } from vue-router import SidebarItem from ./SidebarItem.vue import { useAppStore } from /store/modules/app import { permissionRoutes } from /router/routes const route useRoute() const appStore useAppStore() const sidebarRoutes computed(() permissionRoutes) /script所有菜单项单独拆成递归组件SidebarItem.vue它判断当前路由是否有children有子路由就渲染el-sub-menu否则渲染el-menu-item。这里有一个关键点——el-menu上要加router属性这样点击el-menu-item时会自动调用router.push不需要手动写 click 事件简洁多了。unique-opened属性确保同一时间只展开一个一级菜单避免页面太长滚动时多个菜单同时展开显得混乱。有一个经验点路由的 meta 信息决定菜单展示时path 一定不要写死成绝对路径特别是子路由的 children 里用相对路径。否则做了动态权限控制后菜单点击跳转时路由嵌套层级会错位。3.3 顶栏折叠按钮、面包屑、全屏操作顶栏左侧一般放两个东西侧边栏折叠按钮和面包屑导航。折叠按钮的逻辑很简单点击时调用 Pinia 里的toggleSidebar然后侧边栏通过:collapse响应式切换。面包屑导航在后台系统里是个容易忽视的细节。做法是遍历当前路由的route.matched拿到每一级路由的meta.title进行拼接。这里有个易错点route.matched数组的元素里带有父级 layout 的信息需要根据meta.title是否存在来决定要不要显示。如果一级路由没有标题面包屑就会多出一个空白项。我写的面包屑组件长这样!-- Breadcrumb/index.vue -- template el-breadcrumb separator/ el-breadcrumb-item v-for(item, index) in breadcrumbItems :keyitem.path :toindex breadcrumbItems.length - 1 ? undefined : item.path {{ item.meta.title }} /el-breadcrumb-item /el-breadcrumb /template script setup langts import { computed } from vue import { useRoute } from vue-router const route useRoute() const breadcrumbItems computed(() { return route.matched.filter(item item.meta item.meta.title) }) /script这里要注意最后一个面包屑项是当前页不应该再是可点击的链接。:to要特殊处理成undefined。顶栏右侧一般放全屏按钮、消息通知图标、用户下拉菜单。Element Plus 的el-dropdown做用户菜单非常方便里面放个人中心、退出登录等操作。实现全屏功能可以用原生 APIdocument.documentElement.requestFullscreen()配合退出全屏的document.exitFullscreen()封装一个 hook两三行代码就搞定。骨架和顶栏都要有z-index的层级管控。Element Plus 的el-header默认有阴影如果里面放下拉内容你会发现被后面的内容遮挡给顶栏设置position: relative和z-index: 9就可以规避大部分层级问题。3.4 主内容区与多级路由缓存主内容区是简单的router-view嵌套但我加了一层过渡动画和 keep-alive 缓存!-- AppMain.vue -- template section classapp-main router-view v-slot{ Component, route } transition namefade-transform modeout-in keep-alive :includecachedViews component :isComponent :keyroute.fullPath / /keep-alive /transition /router-view /section /template页面缓存这个东西后台系统需求比较常见列表页面可能滚动了一百条数据用户去详情页看一眼再返回列表数据还在比重新加载体验好得多。但缓存也不能一锅端因为不是所有页面都需要缓存有些页面每次进入都要刷新数据。所以我把缓存列表也放到了 Pinia 里叫cachedViews由各页面自己决定是否进入缓存队列。严格来说这个属于后续功能扩展的范畴但布局阶段就要把壳子预留好。过渡动画我用了fade-transform就是一个透明度结合轻微位移动画让页面切换不突兀但也不会过于花哨。后台系统讲究的是效率动画太多反而拖慢节奏。3.5 侧边栏折叠与响应式适配折叠功能看着简单实现时还是有些细节的。el-menu的collapse属性控制折叠折叠后宽度从220px变成64pxel-aside的宽度要同步联动。这里有个容易踩的坑折叠动画期间菜单文字会换行错乱或者卡顿需要在折叠状态下隐藏 logo 文案只保留图标。我测试下来给el-menu加collapse-transition属性设为 false 能解决大部分动画卡顿问题因为 Element Plus 自带的折叠过渡和 aside 的宽度过渡叠在一起会出现抖动。el-menu :collapseappStore.sidebarCollapsed :collapse-transitionfalse 响应式适配这块后台管理系统和 C 端网站的响应式策略不同不需要特别复杂的断点。大多数后台使用场景是 PC 浏览器我做了一个小阈值处理窗口宽度小于 768px 时侧边栏默认收起只显示图标模式折叠态这样在小屏笔记本上也能腾出空间给内容区。// useResponsive.ts import { onMounted, onBeforeUnmount } from vue import { useAppStore } from /store/modules/app export function useResponsiveSidebar() { const appStore useAppStore() const handleResize () { const width document.documentElement.clientWidth if (width 768) { appStore.sidebarCollapsed true } else { appStore.sidebarCollapsed false } } onMounted(() { handleResize() window.addEventListener(resize, handleResize) }) onBeforeUnmount(() { window.removeEventListener(resize, handleResize) }) }这个 hook 在layout/index.vue里调用。第一次加载时就立即执行一次handleResize确保刷新页面后能根据当前窗口宽度恢复正确的折叠状态否则用户上次折叠了侧边栏刷新后又展开体验很割裂。4. Dashboard 首页内容设计与路由联动4.1 首页 Dashboard 内容区设计布局相关的内容搭建完成后就到了首页具体内容的实现。Dashboard 分析页我放了四个统计卡片和两张常见图表访问量趋势折线图、用户来源饼图。统计卡片用el-card配合el-row、el-col栅格布局排列。栅格的关键在于响应式断点后台表格页通常需要:span6加上xs属性让其在手机上变成:xs12这样在窄屏下也可以两列排布不至于挤成一团。el-row :gutter16 el-col :xs12 :sm12 :md6 v-forcard in cards :keycard.title el-card shadowhover classstat-card !-- 卡片内容 -- /el-card /el-col /el-row统计卡片我这里没直接用 Element Plus 自带的样式而是定义了一个统一风格的组件库。直接把样式写在style scoped里卡片背景为白色、圆角 8px、标题用 14px 灰色、数字用 24px 加粗。这套风格在 Element 基础上做了微调视觉上更贴合业务需要。图表用的是 ECharts配合 Vue3 可以用vue-echarts封装组件也可以用原生 ECharts 自己管理实例。我倾向在hooks/useEcharts.ts里做一个组合式封装把init、setOption、resize、销毁逻辑单独抽出来因为图表组件在后台系统里复用频率极高表格页、详情页、dashboard 都要用到。// hooks/useEcharts.ts import { onMounted, onBeforeUnmount, Ref, shallowRef } from vue import * as echarts from echarts export function useEcharts(elRef: RefHTMLElement) { const chart shallowRefecharts.ECharts() onMounted(() { chart.value echarts.init(elRef.value) }) const setOption (option: echarts.EChartsOption) { chart.value?.setOption(option) } const resize () { chart.value?.resize() } onBeforeUnmount(() { chart.value?.dispose() }) return { setOption, resize } }需要注意 ECharts 的体积问题。按需引入 ECharts 模块能有效减小打包体积不然整个 echarts 包全量引入构建出来会多出 1MB 以上import { LineChart, PieChart } from echarts/charts import { GridComponent, TooltipComponent, LegendComponent } from echarts/components import { use } from echarts/core4.2 路由联动菜单高亮与标签页的实现菜单当前项高亮通过el-menu的:default-activeroute.path实现。这个绑定的值就是当前路由的 path组件会读取default-active找到对应的el-menu-item并设置为 active 状态。实际开发中常遇到的问题是嵌套路由下 path 不匹配比如「用户管理」的路由是/system/user但当前路由 path 是/system/user/detail这时候菜单就不会正确高亮。解决方案是把default-active绑定为route.meta.activeMenu || route.path详情页可以指定高亮哪个菜单项。这个属性在meta里声明传菜单对应的路由 path。标签页导航是个相对高级的功能典型的后台管理框架中标签页tag view用于缓存用户打开过的页面方便快速切换。如果首页布局阶段不做后续要加也不难关键是状态管理要设计好用 Pinia 存一个visitedViews数组路由切换时push当前路由信息关闭标签时从数组里移除并跳转到相邻路由。这个功能在布局阶段不需要实现但我在状态管理的类型定义里预留了接口避免后面改动幅度过大。4.3 404 页面与全局路由守卫后台系统 404 页面必须有。用/:pathMatch(.*)*这种 Catch-All 路由匹配规则重定向到 NotFound 页面即可。这个路由必须放在路由表最后否则会拦截所有正常路由。// router/index.ts { path: /:pathMatch(.*)*, name: NotFound, component: () import(/views/error/404.vue), meta: { title: 404 } }全局路由守卫比较重要。登录状态校验、页面标题设置、动态权限路由注册这些逻辑都放在beforeEach里。基础的前提下如果没有登录信息跳转到登录页登录后动态添加路由并重新进入目标路由。页面标题设置为当前路由的meta.title这样浏览器标签页上能显示「用户管理 - P9 Admin」这种有语义的标题。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next({ path: /login }) return } if (token to.path /login) { next({ path: / }) return } // 动态路由注册逻辑... document.title to.meta.title ? ${to.meta.title} - P9 Admin : P9 Admin next() })动态路由注册要注意addRoute之后需要next({ ...to, replace: true })重新进入一次否则第二次刷新页面时路由表可能没有注册上会出现页面空白。这是 Vue Router 4 一个经典坑我在第五部分再展开讲。5. 常见问题与排查技巧实录5.1 菜单高亮不跟随路由变化这个问题的典型现象是路由跳转成功了地址栏变了但是侧边栏菜单的高亮状态没有跟着变。95% 的情况都是default-active绑定的值不是当前路由 path。el-menu的default-active只在初始渲染时生效如果绑定的值在运行期间变了它也会响应式更新所以问题通常出在绑定的值压根就不对。排查方式在侧边栏组件的watch里打印当前route.path和菜单项的index值对比一下看看是不是不一致。如果是在详情页里面上面说的meta.activeMenu就需要派上用场了。菜单的index建议直接用路由完整路径不要自定义无关字符串避免和default-active对不上。5.2 侧边栏折叠后菜单文字残留或图标错位el-menu折叠后有时候会残留文字或者子菜单弹层位置不对。两个解决要点第一给el-menu加collapse-transition为 false这是我最常用的办法第二确认el-aside的overflow设置为hidden同时.el-menu--collapse样式下子菜单的宽度不要设置固定值而是根据父容器自适应。如果弹层位置不对大概率是el-menu的popper-class没配对或者是侧边栏的transform属性导致的定位上下文变化。最简单粗暴的做法是给弹层菜单单独设置append-to-bodyElement Plus 的菜单子项支持这个属性。5.3 按需导入后组件样式丢失配置了unplugin-vue-components之后某些组件常见的是ElMessage、ElMessageBox、ElLoading样式丢失弹出消息显示裸 HTML。原因是这些命令式组件的样式没有被插件自动引入。解决方案是在入口文件main.ts或者全局样式文件里手动引入import element-plus/es/components/message/style/css import element-plus/es/components/message-box/style/css import element-plus/es/components/loading/style/css另一个边界情况是使用ElConfigProvider配置国际化时如果单独引入日期相关的组件dayjs的语言包没有配置日期选择器会显示英文。记得在 main.ts 里引入import element-plus/es/components/date-picker/style/css import locale from element-plus/es/locale/lang/zh-cn5.4 路由切换后页面空白首次进系统一切正常刷新后页面白屏或者控制台报No match found for location with path。这就是路由注册和刷新顺序的问题。如果使用动态路由addRoute注册刷新时路由表是空的但这个空状态是异步填写的Vue Router 还需要重新触发一次导航。标准的写法是// 递归生成动态路由后 dynamicRoutes.forEach(route { router.addRoute(layout, route) }) // 重新进入一次确保路由匹配成功 next({ ...to, replace: true })如果已经用了next({ ...to, replace: true })还是白屏看下是不是路由嵌套层级不对。用addRoute(layout, route)时将组件挂到了 layout 的嵌套router-view下一定要确认 route 的 path 和当前访问的路径能匹配上不然到不了router-view渲染组件页面自然空白。注意Vue Router 4 中动态添加的路由要挂到指定父路由下如果不传父路由名会直接注册到根路由层。根路由没有router-view组件就不会渲染。5.5 页面缓存 keep-alive 不生效或者乱缓存keep-alive的include数组存的是组件的name不是路由的 name。很多新手在路由的meta里配了name结果缓存不生效。Vue3 中要缓存生效必须在组件的script中显式声明name使用script setup时还需要借助defineOptionsscript setup langts defineOptions({ name: DashboardAnalysis }) /scriptinclude数组里的字符串必须和这个 name 完全一致。排查的时候先确认有没有写defineOptions这个在script setup下特别容易漏。6. 布局完成后的验证与调试经验布局做完不是说你看到页面挺像样就完事了我建议做一轮系统性的验证。第一检查所有一级菜单、二级菜单的路由跳转是否正常文章里递归菜单有时候子路由的path写错一个前缀js 又不会报错页面就一直 404。第二逐项点击菜单确认高亮、面包屑、标签页、keep-alive 缓存之间的状态联动是否一致。第三用浏览器无痕窗口跑一遍登录 → 进入首页 → 刷新 → 跳转核心页面 → 退出登录的完整流程特别是刷新这个动作最能暴露动态路由和状态持久化的问题。调试阶段我会开启vuejsdevtools 面板观察路由状态、Pinia 状态、组件渲染顺序。如果遇到页面布局抖动优先检查 CSS 层级和过渡动画的叠加问题。这类问题通常不是逻辑错误而是样式相互干扰。定位的时候直接用 devtools 的 Elements 面板逐层查看盒模型。7. 性能优化与体验细节补充后台管理系统性能优化我一般关注三个点。第一是路由懒加载。所有业务页面统一用() import(/views/xxx.vue)的方式实现代码分割只会在访问该路由时才加载对应 js chunk。如果不做构建出来的主包会非常大首次打开白屏时间长体验很糟糕。第二是图标按需加载和本地化。Element Plus 自带图标库是 SVG 渲染的虽然体积已经很小但全局注册所有图标没有意义。我用的是element-plus/icons-vue在组件内部直接引入使用这样构建时 tree-shaking 会把没用到的图标移除掉。第三是通用请求封装拦截器。在api/request.ts里用 axios 写了一个统一请求实例设置了baseURL、超时时间、请求拦截器自动携带 token、响应拦截器统一处理错误码。这个不属于首页布局本身但首页布局面板要展示的数据都必须走请求发出去没有统一的基础请求封装功能扩展极不方便。再说一个体验细节侧边栏在菜单项数量超过一屏时需要对菜单区加overflow-y: auto让菜单可以独立滚动而不是带着整个页面一起滚。如果把滚动放在整个布局层顶栏和菜单都会跟着滚非常影响使用体验。Element Plus 的el-menu本身支持容器内滚动配合自定义 scrollbar 样式可以做得更顺滑。这里要注意滚动条样式在 webkit 内核下的自定义写法针对::-webkit-scrollbar做高度和颜色处理不然默认滚动条在深色侧边栏上特别刺眼。8. 扩展思路这套方案还能怎么延伸首页布局这块确实只是整个后台系统的第一步但地基打好了扩展会很舒畅。我个人在实际项目里用过同一套骨架扩展出几种场景这里分享下能做哪些延伸三级甚至更多级的菜单递归组件已经写好直接在路由配置里加嵌套层级就行。不过建议菜单层级别超过三级层级太深用户理解成本极高后台页面浏览效率反而下降。权限控制体系把动态路由注册这部分升级成基于用户角色过滤的模式。调用接口拿用户菜单和权限编码前端根据权限编码动态过滤路由表再注册进 map。动态菜单与权限天然契合。多标签页TabView增加拖拽排序、右键关闭其他/关闭左侧/关闭右侧这些操作参考成熟框架的代码思路即可状态设计往 Pinia 里放数据流是清晰的。我一直觉得后台框架这类东西真正重要的不是框架本身而是里面沉淀下来的约定和边界。哪层做什么事、路由和菜单怎么统一数据源、状态管理和路由如何配合这些规则定好了后面不管接入多少业务页面、多少权限逻辑都不会乱。最后说一个我个人的体会做后台布局前的第一件事不是写代码而是把项目的功能层级先理一遍。一个后台系统有哪些模块、哪些页面、哪些页面有详情页联动、哪些页面需要缓存、哪些菜单是权限控制的这些问题想清楚了布局方案自然水到渠成原本代码里很多左调右调的试错都能在设计阶段规避掉。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。