X-admin实战:从layui后台模板到Vue集成的完整指南
发布时间:2026/9/16 12:21:12 作者:尧图编辑部 阅读量:1,286

简介X-admin是一款基于layui的轻量级经典前端后台管理模板面向各层次前后端程序员用于快速搭建简洁、兼容性好、可定制的后台管理界面。压缩包共160个文件以gif演示图、html页面、js逻辑、css样式为主另含字体图标、配置说明、markdown文档等整体约959KB目录划分清楚便于按需取用。目前已有3872人学习下载。包内包含dist发布目录、src源码目录、demo演示示例、docs文档等覆盖编译前后代码、二次开发素材与完整使用说明结合丰富示例与文档可快速理解响应式布局、模块化组件、主题定制等核心用法。对正在选型或准备二次开发后台系统的开发者来说是一份轻量而实用的参考资源。1. X-admin是什么一个仍然值得抄作业的layui后台模板先抛个反直觉的结论X-admin这个项目代码量不大star也不算顶流但它是很多前后端程序员真正上手后台管理系统时翻的第一个模板。原因不复杂——它把后台管理里最常见的那套东西左侧菜单、顶部tab、内容区iframe、权限菜单、弹层、表格操作用最直白的方式组织起来了不依赖Node环境不要求组件化思维解压就能在浏览器里跑。对于刚接触layui的前端或者后端程序员打算独立搭一个管理后台X-admin的目录结构和初始化流程几乎可以直接拿来做骨架后续换成自己项目的接口和页面就行。这篇就说清楚三件事它的布局和加载机制怎么理解、日常开发里高频的组件操作怎么写、以及它和Vue这一代框架怎么共存。2. X-admin的目录结构与页面加载机制先搞懂它怎么运行的2.1 从index.html到tab页X-admin的默认布局怎么拼出来的X-admin的经典布局不是组件树而是三个独立区域通过iframe拼起来的。打开index.html左手是侧边菜单右手顶部是一条tab标签栏tab对应的内容区域实际上是一个个iframe页面。这种设计的好处是每个业务页面完全隔离A页面的JS报错不会拖垮整个后台菜单和内容区逻辑解耦后端渲染、前端渲染都能接。典型的index.html结构长这样body classlayui-layout-body div classlayui-layout layui-layout-admin !-- 左侧菜单 -- div classlayui-side layui-bg-black div classlayui-side-scroll !-- 这里由menu.js或后端接口动态渲染 -- ul classlayui-nav layui-nav-tree idmenuTree/ul /div /div !-- 右侧主体 -- div classlayui-body !-- 顶部tab栏存放已打开页面的标签 -- div classlayui-tab layui-tab-brief lay-filtertabFilter idtopTabs ul classlayui-tab-title li classlayui-this控制台/li /ul div classlayui-tab-content div classlayui-tab-item layui-show iframe srcwelcome.html>// 菜单数据实际开发中这部分多由后端接口返回 var menuData [ { title: 系统管理, icon: layui-icon-set, children: [ { title: 用户管理, url: user/list.html }, { title: 角色管理, url: role/list.html } ] }, { title: 内容管理, icon: layui-icon-form, url: content/article.html } ]; // 初始化侧边菜单 function initMenu() { var html ; menuData.forEach(function(item) { if (item.children item.children.length 0) { html li classlayui-nav-item; html a hrefjavascript:; lay-tip item.title ; html i classlayui-icon item.icon /i; html cite item.title /cite/a; html dl classlayui-nav-child; item.children.forEach(function(child) { html dda lay-href child.url lay-id child.url child.title /a/dd; }); html /dl/li; } else { html li classlayui-nav-item; html a lay-href item.url lay-id item.url ; html i classlayui-icon item.icon /i; html cite item.title /cite/a/li; } }); document.getElementById(menuTree).innerHTML html; layui.element.render(nav); // 重新渲染导航 }这段代码里有一个非常容易忽略的细节lay-href和lay-id。在X-admin的tab切换逻辑里lay-href决定了点击菜单后iframe要加载哪个URLlay-id用于tab去重。如果lay-id没写或者重复会出现点一次菜单开两个tab、tab关了再点菜单不刷新等问题。菜单渲染完成后必须调用layui.element.render(nav)否则layui不认识新增的DOM菜单点击不会触发任何事件这个问题在新手集成X-admin时出现频率极高。2.3 个人后台接入时最容易踩的目录坑X-admin默认目录里包含css、js、layui、page等文件夹很多人直接把自己的页面丢进去就报错。原因大多是路径问题X-admin的页面间跳转全靠相对路径如果页面放在子目录引用CSS和JS时要按当前页面到根目录的实际距离去计算而不是按index.html所在的位置。目录或文件作用接入时的注意点layui/layui框架本体及内置模块版本别随意升级X-admin的页面是按某个稳定版本写的js/xadmin.jstab切换、菜单联动、iframe管理核心逻辑不要在这个文件里改业务逻辑业务逻辑放到独立JS里page/存放业务页面也就是iframe加载的目标页面子目录页面引用公共资源时用 ../../css/xadmin.css 这种相对路径index.html后台首页框架引入的CSS和JS顺序不能乱jQuery必须在xadmin.js之前一个建议不要改动xadmin.js源码去适配自己的页面。接下来要讲的日期控件、select赋值全部在业务页面里独立处理不要往框架文件里塞代码。3. X-admin与layui组件协作日期控件、select赋值与弹层操作3.1 日期控件最大日期设为当前日期的写法后台管理里最常见的日期需求是不能选未来日期比如配置活动截止时间、查询订单日期。layui的laydate模块用max参数控制可选范围难点在于max需要接收的是字符串而非Date对象并且要在渲染时动态计算。layui.use([laydate, form], function() { var laydate layui.laydate; // 动态获取当天日期的字符串格式必须为 yyyy-MM-dd function getToday() { var d new Date(); var month (d.getMonth() 1) 10 ? 0 (d.getMonth() 1) : (d.getMonth() 1); var day d.getDate() 10 ? 0 d.getDate() : d.getDate(); return d.getFullYear() - month - day; } laydate.render({ elem: #datePicker, max: getToday(), // 禁止选择今天之后的日期 min: 2020-01-01 // 业务上限定可选范围的下界 }); });需要注意max和min支持三种传值固定字符串如2025-12-31、数字0表示今天只能用于内置规则、函数需要返回指定格式的字符串。直接用max: 0配合默认格式在一些版本里是可以的但一旦渲染范围有自定义需求就会失灵所以更稳妥的方式还是先算字符串再传给max。3.2 日期控件点击后日历面板位置偏移的调整思路在X-admin的iframe页面里用laydate经常会出现日历面板弹出位置不对的情况比如偏左、被遮挡甚至跑到可视区域外。这跟laydate默认把面板渲染在body下有关iframe页面自身的滚动容器和X-admin的布局容器对绝对定位的影响不一致。调整思路有三步。先看是不是相对定位的祖先元素导致的laydate的position参数可以指定面板固定停靠方式laydate.render({ elem: #dateTime, position: fixed, // 让面板相对视口定位而不是相对某个父容器 trigger: click // 显式声明点击触发 });如果加了position: fixed还是偏那就手动改面板的定位参数。laydate有个done回调每次选择完日期后会执行但面板刚弹出时没有回调。常见做法是用ready回调它是面板第一次弹出时执行的laydate.render({ elem: #dateRange, ready: function() { // 面板DOM生成后手动修正它的top和left var panel document.querySelector(.layui-laydate); if (panel) { panel.style.left 120px; panel.style.top 80px; } } });ready回调是调整日期面板位置最实用的入口。业务里如果同时存在多个日期控件ready里一定要判断当前触发的是哪个控件不要无差别改所有面板的坐标。另外在X-admin的tab切换场景中如果页面从隐藏状态切回显示日期面板的位置可能因为容器尺寸变化而错位此时可以监听iframe的load事件重新渲染日期控件但注意避免重复初始化。3.3 select动态赋值改了数据别忘了form.renderX-admin基于layuiselect的操作不走原生DOM而是走layui的form模块。很多人给select动态赋值后发现页面没变化原因就是没有调用form.render(select)。Layui在初始化时会把select渲染成自定义样式的div结构原生appendChild或innerHTML赋值后界面不会自动同步。// 假设页面上有一个下拉框 idcategorySelect $.ajax({ url: /api/category/list, dataType: json, success: function(res) { var options ; res.data.forEach(function(item) { options option value item.id item.name /option; }); $(#categorySelect).html(options); // 关键渲染完成后必须重新渲染select layui.form.render(select); } });这里有两个细节值得展开。第一个是赋值时机如果页面初始化时layui的form.render()已经执行过一次后续动态改select内容后只需要再次调用form.render(select)不需要整表重渲染性能开销小很多。第二个是联动场景比如选择省后再拉取市列表每次拉取完成都要重新render否则新选项不显示。如果在X-admin的弹层里操作select弹层内的form也要在弹层内容渲染完成后单独调用一次form.render()因为弹层的内容对主页面来说是新插入的DOM。3.4 表格操作列的常见动作拆解X-admin项目里经常配合layui的table模块做列表页。操作列一般有删除、编辑、审核这类按钮做法是给按钮加lay-event属性再通过table.on(tool(...))监听。推荐把操作列的按钮分发集中在一个函数里代码可读性和维护性都更好table.on(tool(userTableFilter), function(obj) { var data obj.data; var layEvent obj.event; if (layEvent edit) { // 打开编辑弹层数据通过data传递 layer.open({ type: 2, title: 编辑用户, content: user/edit.html?id data.id, area: [600px, 420px] }); } else if (layEvent delete) { layer.confirm(确认删除用户「 data.name 」, function(index) { // 这里调删除接口 $.post(/api/user/delete, { id: data.id }, function(res) { if (res.code 0) { obj.del(); // 删除当前行数据 layer.close(index); } }); }); } });注意obj.del()删除的是表格当前页的数据行不会自动同步到数据库。正经的删除流程是先发接口请求成功后再用obj.del()刷新视图不要让用户看到行没了但刷新又回来了的假象。4. 把X-admin接进现代前端与Vue、Webpack协作的可行路径4.1 layui可以用vue吗的正解这个问题在后台管理开发里被反复问答案是可以用但不能把layui塞进Vue的响应式系统里。Layui的form、date等模块直接操作DOM而Vue通过虚拟DOM管理页面两者同时操作同一块DOM区域会产生冲突——最典型的表现是Vue更新数据后layui渲染的select样式丢失layui赋值后Vue的响应式数据没有同步。解决思路有几个大项目用Element UI或Ant Design不谈小项目想保留X-admin的现成布局就把两者分离。分离的边界是Vue只负责业务页面的数据逻辑layui负责用户交互组件下拉框、日期、弹层二者通过事件和回调通信不共享DOM结构。4.2 在Vue页面里按需加载layui模块的最小写法如果业务页面是用Vue写的但又想用layui的layer弹层最干净的方式是通过layui.use在Vue的生命周期里初始化并在组件销毁时清理弹层。// Vue组件内部写法以日期控件为例 export default { name: OrderForm, mounted() { // 动态加载layui模块避免在组件里全局引入 if (window.layui) { layui.use([laydate, form], () { layui.laydate.render({ elem: #orderDate, max: 0 }); // 给select动态赋值并渲染 $(#statusSelect).html(option value1待支付/option); layui.form.render(select); }); } }, beforeDestroy() { // 关闭所有由当前页面打开的layer弹层 if (window.layer) { layer.closeAll(); } } }注意layui.use的回调是异步的不能保证在mounted里同步拿到laydate模块。这里把初始化逻辑全部放在layui.use的回调里配合jQuery的DOM操作。另外一个值得记下的习惯在beforeDestroy里调用layer.closeAll()。X-admin的弹层和iframe页面共用一个层管理器如果切换路由或关闭tab时还记得关掉弹层后台会越用越卡。4.3 前端路由与X-admin的iframe布局怎么折中X-admin的iframe布局与Vue Router天然互斥因为iframe里加载的是独立HTML文件不是路由组件。如果想让X-admin的骨架保留、同时在内容区跑Vue应用常见做法是在iframe的src里指向Vue应用的入口URLVue应用内的路由使用history或hash模式自己管理。左侧菜单点击 - lay-hrefhttp://localhost:8080/#/order/list - iframe加载Vue应用 - Vue Router接管内部页面这个方案能很好的平衡前后端协作X-admin的菜单和tab逻辑不用改Vue应用只需要处理自己内部的业务路由。需要注意跨域问题——如果Vue应用和X-admin不在同一个端口iframe跨域后X-admin无法获取iframe内部页面的URLtab的地址栏同步功能就会失效。开发环境可以靠代理解决生产环境建议把Vue打包后的产物直接放进X-admin的page目录下。4.4 静态页面与动态路由的分离实践对于接口驱动的后台把菜单和路由全部交给后端是最直接的做法。X-admin在后端只返回菜单数据前端按菜单渲染的模式下表现不错因为前端不需要路由守卫菜单本身就是权限。场景X-admin的推荐做法不推荐的做法菜单权限后端登录后返回菜单JSON前端渲染nav前端写死菜单再用v-if控制显示按钮权限后端返回按钮标识集合前端判断是否渲染按钮只隐藏不校验接口层无权限控制路由跳转iframe地址由菜单项的lay-href控制用Vue Router管理整个后台的页面5. 让X-admin用得更顺的五个检查点从页面加载顺序到权限控制5.1 检查jQuery与layui的引入顺序X-admin页面里最容易出现的问题集中在引入顺序上。xadmin.js依赖jQuery而layui的部分模块也依赖jQuery。推荐顺序是jQuery先引入然后引入layui的layui.js这一行会自动加载核心模块最后引入xadmin.js。如果顺序错了会出现layer is not defined或layui is not defined排查时直接从HTML头部开始检查。script src../../lib/jquery-3.4.1.min.js/script script src../../layui/layui.js/script script src../../js/xadmin.js/script5.2 检查iframe页面里layer弹层的归属X-admin的弹层默认渲染在顶层页面还是iframe内部取决于layer.open的type参数。type为2页面层时如果iframe的URL是自己的业务页面弹层会挂在iframe内部遮罩只覆盖iframe区域想要全屏遮罩需要把弹层的content指定为顶层页面调用。遇到弹层显示不全、被tab遮挡的情况优先检查弹层是在哪个文档流里打开的。5.3 检查刷新页面后tab是否保留了正确状态X-admin有一个比较隐蔽的问题刷新浏览器后iframe重新加载tab栏可能恢复成默认状态。要解决这个问题通常的做法是在xadmin.js的基础上在localStorage里记录当前打开的tab列表和选中状态刷新后重新读取并恢复iframe的src。下面是一段可以直接插入个人后台的兼容性代码// 页面加载完成后恢复tab状态 window.addEventListener(load, function() { var savedTabs localStorage.getItem(xadmin_tabs); if (savedTabs) { var tabData JSON.parse(savedTabs); // 重新创建tab标题和对应iframe tabData.forEach(function(item) { // 调用xadmin的addTab方法需在xadmin.js中确认具体方法名 if (typeof addTab function) { addTab(item.title, item.url); } }); } }); // 监听tab切换保存当前状态 document.querySelector(.layui-tab-title).addEventListener(click, function(e) { var currentTab document.querySelector(.layui-tab-title .layui-this); var currentFrame document.querySelector(.layui-tab-content .layui-show iframe); if (currentTab currentFrame) { localStorage.setItem(xadmin_tabs, JSON.stringify([{ title: currentTab.innerText, url: currentFrame.src }])); } });这段代码不做全量状态恢复只恢复当前正在浏览的tab因为全量恢复涉及tab关闭顺序、iframe懒加载等复杂逻辑小项目中保存当前正在看的这一页就够用了。5.4 检查数据表格的渲染方式区分刷新与重载业务开发中经常有人分不清table.reload和table.render。table.render会把表格的完整配置重新执行一遍包括分页组件、数据请求等table.reload只更新表格数据保留原有表格配置。在X-admin的iframe页面里查询按钮点击后应该用table.reload(tableId, { where: { keyword: xxx } })而不是重新render否则表格的分页状态、排序状态全部丢失。5.5 兜底检查layui版本锁定X-admin的页面是按某个特定版本的layui写的后续layui官方升级部分模块的API变了比如laydate的max参数格式、table模块的parseData回调。个人后台接入时最稳妥的做法是保留X-admin自带的layui文件夹业务页面的新功能基于这个版本开发。如果确实需要升级layui版本优先回归测试三个地方日期面板是否偏移、select的样式是否错乱、table操作列的点击事件是否正常。测试通过后再覆盖到所有业务页面。本文还有配套的精品资源点击获取