注意: 必须以http://的形式访问,而不是file://的形式访问。
main.js为入口js,上手使用项目前最好先看看它的说明。
index.html就是这样固定的结构,一般不需要修改,只用在components下写子页面即可。
3.1.6版本开始菜单支持更丰富的写法:
[{ "name": "xx管理", "url": "#/xxx" "show": false }, { "name": "百度", "url": "#/baidu", "iframe": "https://baidu.com" }, { "name": "layui", "url": "https://layui.com", "target": "_blank" }] show false表示只注册路由,不显示在侧边栏iframe 表示用iframe打开一个标签target 表示当url不受路由控制时在新窗口打开链接注意: url以"#/"开头才会受路由控制,实现局部加载。
main.js的业务流程为先通过ajax加载菜单json,然后注册路由、渲染侧边栏、加载主页,至此框架即成功运行了。
如果不是前后端分离的项目,base怎么配置加载模块都是404,建议这样配置:
layui.config({ base: getProjectUrl() + 'assets/module/' }) /* 获取项目根路径 */ function getProjectUrl() { var layuiDir = layui.cache.dir; if (!layuiDir) { var js = document.scripts, last = js.length - 1, src; for (var i = last; i > 0; i--) { if (js[i].readyState === 'interactive') { src = js[i].src; break; } } var jsPath = src || js[last].src; layuiDir = jsPath.substring(0, jsPath.lastIndexOf('/') + 1); } return layuiDir.substring(0, layuiDir.indexOf('assets')); }getProjectUrl()方法原理是获取layui.js的全路径,并截取assets之前的路径,所以layui需要在assets目录下面, assets上一层需要是项目根路径,或者根据你的目录结构合理修改getProjectUrl方法,比如assets改成static。
setter模块主要是用于给index模块和admin模块提供配置,可根据自己需要进行更改。
参数:
配置名默认说明baseServer'json/'接口地址,admin.req方法会自动加tableName'easyweb-spa'前端缓存用的存储表名pageTabsfalse是否开启多标签cacheTabtrue是否记忆TabopenTabCtxMenutrue是否开启Tab右键菜单maxTabNum20最多打开多少个tabviewPath'components'视图位置viewSuffix'.html'视图后缀defaultTheme 默认主题reqPutToPosttruereq请求put方法变成post,delete变get,并自动加_methodapiNoCachetrue配置为true后ajax请求json数据不会加版本号navArrow 侧边栏导航箭头,可选''(layui默认)、'arrow2'(箭头)、'arrow3'(加减号)closeFooterfalse是否关闭页脚tplOpen'{{'模板引擎边界符tplClose'}}'模板引擎边界符tabAutoRefreshfalse是否切换Tab自动刷新页面defaultLoading1默认的加载动画(只控制admin.showLoading的默认)方法:
配置名默认getToken()获取tokenputToken(token)缓存tokenremoveToken()清除tokengetUser()获取当前登录的用户信息putUser(user)缓存当前登录的用户信息getUserAuths()获取用户所有权限getAjaxHeaders(requestUrl)ajax请求的统一headerajaxSuccessBefore(res, requestUrl)ajax请求结束后的统一预处理routerNotFound(r)路由不存在处理setter模块里面的配置都是默认配置,如果在主题界面修改了配置会以主题界面操作的为主,主题界面的操作保存在本地缓存中, 清除浏览器缓存就会以setter模块里面配置的为主。
table需要在setter.js最下面用table.set实现自动传递header和预处理:
layui.define(['table'], function (exports) { var setter = { }; /* table全局设置 */ var token = setter.getToken(); if (token && token.access_token) { layui.table.set({ headers: {'Authorization': 'Bearer ' + token.access_token}, parseData: function(res) { // 利用parseData实现预处理 if(res.code == 401) { setter.removeToken(); layui.layer.msg('登录过期', {icon: 2, anim: 6, time: 1500}, function () { location.replace('components/template/login/login.html'); }); } return res; } }); } exports('setter', setter); });dataGrid和treeTable组件是支持参数配置使用admin.ajax的发送请求的。
或者使用$.ajaxSetup统一处理:
$.ajaxSetup({ complete: function (xhr) { try { var res = JSON.parse(xhr.responseText); if (1131 === res.code || 1122 === res.code || 1120 === res.code) { // 登录过期 setter.removeToken(); layui.layer.msg('登录过期', {icon: 2, anim: 6, time: 1500}, function () { location.replace('components/template/login/login.html'); }); } } catch (e) { } } });注意: layui更新到2.5.6后模块名不能使用config,所以config.js现在改成了setter.js,请注意修改
// 老版本只用把前面两行的config改成setter就可以了 layui.use(['setter'], function(){ var config = layui.setter; }); /* layui.use(['config'], function(){ var config = layui.config; }); */
SPA单页面应用如果有重复的ID会出现问题,所有的页面都不能有重复的ID,因为是jQuery系的框架,id还是很常用的, 为了避免ID出现重复,强烈建议按照一定规范来命名。
总体命名规则xxxXxxXxx
第一个xxx 模块名,比如用户模块user第二个Xxx 功能描述,比如添加Add第三个Xxx 元素类型,比如按钮Btn例如页面是user.html,它的各个元素命名如下:
按钮的命名
<!-- 命名规则:xxxXxxBtn --> <button id="userAddBtn" class="layui-btn">添加</button> <button id="userEditBtn" class="layui-btn">修改</button> <button id="userDelBtn" class="layui-btn">删除</button>表格的命名
<!-- 命名规则:xxxTable --> <table id="userTable" lay-filter=""userTable"></table> <!-- 表格上方搜索表单,命名规则:xxxTbSearchForm,id和lay-filter是非必须的 --> <form class="layui-form" id="userTbSearchForm" lay-filter="userTbSearchForm"> <!-- 输入框部分省略... --> <!-- 搜索提交按钮命名规则:xxxTbSearch --> <button class="layui-btn" lay-filter="userTbSearch" lay-submit>搜索</button> </form> <!-- 表格操作列,命名规则:xxxTbBar --> <script type="text/html" id="userTbBar"> <a class="layui-btn" lay-event="edit">修改</a> <a class="layui-btn" lay-event="del">删除</a> </script> <!-- 表格其他复杂列,例如switch状态开关等,命名规则:xxxTbXxx --> <script type="text/html" id="userTbState"> <input type="checkbox" lay-filter="userTbStateCk" value="{{d.userId}}" lay-skin="switch" lay-text="正常|锁定" {{d.state==0?'checked':''}}/> </script>一个页面有多个表格的命名
<!-- 命名规则:xxxXxxTable --> <!-- 例如这是一个用户订单记录表 --> <table id="userOrderTable" lay-filter=""userOrderTable"></table> <!-- 表格操作列,命名规则:xxxXxxTbBar --> <script type="text/html" id="userOrderTbBar"> <a class="layui-btn" lay-event="edit">修改</a> <a class="layui-btn" lay-event="del">删除</a> </script>表单弹窗的命名
<!-- 页面层弹窗命名规则:xxxEditDialog,非页面层可以忽略script --> <script type="text/html" id="userEditDialog"> <!-- 表单命名规则:xxxEditForm --> <form id="userEditForm" lay-filter="userEditForm" class="layui-form model-form"> <!-- 输入框部分省略... --> <!-- 提交按钮命名规则:xxxEditSubmit --> <button class="layui-btn" lay-filter="userEditSubmit" lay-submit>保存</button> </form> </script>页面其他弹窗的命名
<!-- 例如这是一个查看详情弹窗 --> <!-- 页面层弹窗命名规则:xxxXxxDialog,非页面层可以忽略script --> <script type="text/html" id="userInfoDialog"> <!-- 表单命名规则:xxxXxxForm --> <form id="userInfoForm" lay-filter="userInfoForm" class="layui-form model-form"> <!-- 输入框部分省略... --> <!-- 提交按钮命名规则:xxxXxxSubmit --> <button class="layui-btn" lay-filter="userInfoSubmit" lay-submit>保存</button> </form> </script>表单里面的input建议少用id,只用name,取input的值通过监听表单提交来取,用input通过$('#xxxForm [name="xxx"]')来写。
其他元素ID命名
<!-- 命名规则:xxxXxxXxx,第一个xxx是模块名,第二个Xxx是功能描述,第三个Xxx是元素类型 --> <!-- 例如角色选择的下拉框 --> <select id="userRoleSel"></select> <!-- 例如备注的输入框 --> <input id="userCommentsEdt" />元素类型简写
元素简写描述按钮类Btn<button>、.layui-btn等输入类的EdtEditText的简写,input、textarea等选择类的Sel下拉框、多选下拉框等表格Tableid="userTable"、id="userOrderTable"等表格附加组件Tbid="userTbBar"、id="userOrderTbBar"等弹窗Dialogid="userEditDialog、id="userInfoDialog"等表单Formid="userEditForm、id="userInfoForm"等表单提交按钮Submitid="userEditSubmit"、id="userInfoSubmit"等页面的命名
<!-- 命名格式:xxxApp --> <div class="layui-fluid" id="userApp"> <!-- 省略页面内容...... --> </div>以上规则只是规范,非必须要求
在子页面首行增加<tpl/>标签即可开启模板引擎:
<tpl/> <style> </style> <div class="layui-fluid"> {{# if(d.search.id==1){ }} <span>aaa</span> {{# }else{ }} <span>bbb</span> {{# } }} </div> <script> </script>用法与laytpl一致,边界符可以在setter.js中修改,d表示的是当前路由信息,例如访问 #/system/user/id=1/sex=男 它的路由信息为:
{ path: ["system", "user"], search: {id: 1, sex: "男"}, href: "/system/user/id=1/sex=男", refresh: false } path 路径数组search 参数列表refresh 如果是刷新触发的值为true也不一定只有d能用,比如获取layui版本号:{{layui.v}}
参数是一个数组,当你访问#/system/user时,就会打开一个“用户管理”标签页,“url”是路由关键字, 对应的页面地址是“components/system/user.html”,“components”和“.html”可以在setter模块中配置。
只有调用了loadHome方法,路由模块才会真正开始工作。
也可以使用<a href="#/system/user">XX</a>,这种写法必须保证“#/system/user”已经注册了路由,而“openNewTab()”会自动注册。
注意:openNewTab方法只是注册临时路由,如果刷新页面,路由就会不存在了,
如果想刷新页面也能使用,需要提前在main.js中的index.loadHome()方法之前注册路由, 或者在menus.json中配置,并且可以加show: false不在侧边栏中显示。
#/可写可不写,关闭当前tab可以用admin.closeThisTabs()或者ew-event="closeThisTabs"。
前面#/可写可不写。
关闭多标签会自动生成一个标题栏,可用此方法修改标题栏,参数为undefined为隐藏标题栏。
这两种hash最后返回的都是system/user,后面属于参数传递,不属于视图的路径。
在layui-nav-tree上面增加lay-shrink="_all"配置即可展开菜单时收缩兄弟节点:
<!-- 侧边栏 --> <div class="layui-side"> <div class="layui-side-scroll"> <ul class="layui-nav layui-nav-tree" lay-shrink="all"> ......省略其他部分 </ul> </div> </div>注意:是_all而不是all,在3.1.6版本之前是lay-accordion="true"。
setter.js里面配置tabAutoRefresh: true即可开启。
3.1.6版本开始在index模块封装了渲染侧边栏方法,不用自己写laytpl渲染:
admin.req('menus.json', {}, function (res) { index.renderSide(res); // 渲染侧边栏 }, 'get');该方法还提供了灵活自定义操作,比如多系统模式,自定义要怎么插入渲染完的html:
index.renderSide(res, $('#sideNav').html(), function(html, obj){ // html 是渲染后的内容 // obj.data 可获取处理后的数据,比如移除了show: false的数据 // obj.side 是侧边栏的dom,例如$(obj.side + '>.layui-nav').html(html) // obj.render() 可更新nav的渲染 }, '{{', '}}'); 参数一 必填,数据参数二 非必填,html模板参数三 非必填,hyml渲染完的自定义处理参数四 非必填,模板引擎边界符参数五 非必填,模板引擎边界符index.renderSide一般写在index.regRouter之后,index.regRouter还可以格式化数据
使用示例:
layui.use(['admin'], function () { var admin = layui.admin; var pageHeight = admin.getPageHeight(); // 获取浏览器高度 });
open和popupRight方法:
// 打开弹窗 admin.open({ type: 2, content: 'tpl-theme.html' }); // 打开右侧面板 admin.popupRight({ type: 2, content: 'tpl-theme.html' });这两个方法只是对layer.open进行了一层封装,参数和layer一样,查看layer文档。
新增参数url:
admin.open({ title: 'Hello', url: 'tpl-theme.html' }); admin.popupRight({ url: 'tpl-theme.html' });type:2, content:xxx这种是iframe类型的弹窗,使用url会通过ajax加载页面到弹窗中,而不是iframe嵌入。 当使用url方式的时候,弹窗页面应该是代码片段,而不是完整的html,如下所示:
<style> * { color: red; } </style> <div class="layui-form model-form"> <button class="layui-btn" ew-event="closeDialog">关闭我</button> </div> <script> layui.use(['layer', 'form'], function () { var $ = layui.jquery; }); </script>页面不需要html、body这些东西,并且可以直接用<script>标签来写事件。
admin.iframeAuto()方法:
针对type:2的弹窗自适应弹窗高度,写在弹窗的子页面中,此方法是调用一次做一次高度自适应, 如果你用js动态修改了弹窗子页面的高度,需要再调用一次。
admin.closeThisDialog():
关闭当前iframe类型弹窗,针对type:2的弹窗,在弹窗的子页面调用即可关闭当前的iframe弹窗。
admin.closeDialog('#xxx'):
关闭非iframe类型的弹窗,调用需要传递弹窗页面里面任意一个的元素。
关闭弹窗还可以使用ew-event操作:
<!-- 关闭弹窗(智能) --> <button ew-event="closeDialog"></button> <!-- 关闭iframe类型的弹窗 --> <button ew-event="closeIframeDialog"></button> <!-- 关闭页面层的弹窗 --> <button ew-event="closePageDialog"></button>使用url方式更符合单页面,admin模块还封装了弹窗参数传递等更多方法,请到弹窗专题查看。
按钮loading:
admin.btnLoading('#btn1'); // 设置按钮为loading状态 admin.btnLoading('#btn1', false); // 移除按钮的loading状态 admin.btnLoading('#btn1', ' 保存...'); // 设置按钮为loading状态,同时修改按钮文字页面加载层:
admin.showLoading('#xxx'); // 在id为xxx的元素中显示加载层 admin.showLoading('#xxx', 1, '.8'); // 显示type为1、透明度为0.8的遮罩层 // 尺寸控制,提供有两种尺寸,默认是sm小型尺寸,还可以选md大型尺寸 admin.showLoading({ elem: '#xxx', type: 1, size: 'sm' }); 参数一 elem 非必填 元素选择器,不填为body参数二 type 非必填 动画类型(1 小球,2 魔方,3信号,4简约),默认1参数三 opacity非必填 透明度(0 - 1),默认不透明参数四 size 非必填 尺寸,默认sm移除加载动画:
admin.removeLoading('#xxx'); admin.removeLoading('#xxx', true, true); 参数一 非必填 元素选择器,不填为body参数二 非必填 true是淡出效果,false直接隐藏,默认是true参数三 非必填 true是删除,false是隐藏不删除,默认是false页面载入的加载动画:
<body class="page-no-scroll"> <!-- page-no-scroll这个不要忘了 --> <!-- 小球样式 --> <div class="page-loading"> <div class="ball-loader"> <span></span><span></span><span></span><span></span> </div> </div> <!-- 魔方样式 --> <div class="page-loading"> <div class="rubik-loader"></div> </div> <!-- 信号样式 --> <div class="page-loading"> <div class="signal-loader"> <span></span><span></span><span></span><span></span> </div> </div> <!-- 简约样式 --> <div class="page-loading"> <div class="layui-loader"> <i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i> </div> </div> <!-- 加sm是小型尺寸 --> <div class="page-loading"> <div class="signal-loader sm"> <span></span><span></span><span></span><span></span> </div> </div> </body>写在页面中需要在js中调用admin.removeLoading()移除加载动画。
req方法:
admin.req('url', { username: 'admin', password: '123456' }, function(res){ alert(res.code + '-' + res.msg); }, 'get', { headers: {} }); 参数一 请求的url,前面会自动加setter.baseServer参数二 非必填,请求参数参数三 请求回调(网络错误也进此回调,404、403等)参数四 非必填,请求方式,get、post、put、delete等,默认get参数五 非必填,ajax的更多参数,如headers、dataType等req的这五个参数是自动像左补齐的,可以简写:
// 无参数,get请求 admin.req('url', function(res){ console.log(res); }); // post提交 admin.req('url', function(res){ console.log(res); }, 'post'); admin.req('url', { username: 'xxx', password: '123456' }, function(res){ console.log(res); }, 'post'); // 参数使用JSON.stringify()会自动增加contentType为json admin.req('url', JSON.stringify(obj), function(res){ console.log(res); }, 'post'); // 相当于 admin.req('url', JSON.stringify(obj), function(res){ console.log(res); }, 'post', { contentType: 'application/json' }); // 参数使用表单序列化 admin.req('url', $('#demoForm').serialize(), function (res) { console.log(res); }, 'post', { contentType: 'application/x-www-form-urlencoded' });ajax方法,参数同$.ajax:
admin.ajax({ url: 'url', data: {}, headers: {}, type: 'post', dataType: 'json', success: function(res){ alert(res.code + '-' + res.msg); } });自动传递header和预处理:
在module/setter.js(原config.js)中通过下面两个方法操作:
var setter = { // 自动传递header getAjaxHeaders: function (url) { var headers = []; headers.push({name: 'token', value: 'xxxxx'}); return headers; }, // 请求完成后预处理 ajaxSuccessBefore: function (res, url, obj) { if(res.code == 401) { alert('登录超时,请重新登录'); // obj.reload(); // 重新发起请求 // obj.update({}); // 修改res数据 // obj.xhr // ajax原始的xhr对象 return false; // 返回false阻止代码执行 } return true; } };req和ajax都实现了自动传递header、预处理、网络错误也回调到success(404、500等错误)。
如果是自定义锁屏页面,页面不要包含head、body这些东西,只是代码片段,参考默认的锁屏页面。
使用示例:
<a ew-event="fullScreen">全屏</a> <a ew-event="flexible">折叠导航</a> 事件描述flexible折叠侧导航refresh刷新主体部分closeThisTabs关闭当前选项卡closeOtherTabs关闭其他选项卡closeAllTabs关闭全部选项卡leftPage左滚动选项卡rightPage右滚动选项卡 closeDialog关闭当前弹窗(智能)closeIframeDialog关闭当前iframe层弹窗closePageDialog关闭当前页面层弹窗 theme打开主题设置弹窗note打开便签弹窗message打开消息弹窗psw打开修改密码弹窗logout退出登录 fullScreen全屏切换back浏览器后退lockScreen锁屏 open打开弹窗popupRight打开右侧弹窗ew-event属性可用于任何元素,不仅仅是a标签,theme、note等可以通过data-url属性配置对应的url, 还可以通过data-window="top"属性配置在父页面处理事件。
<a ew-event="theme" data-url="xxx.html" data-window="parent">主题</a>自定义事件,添加下面代码即可,建议放在main.js中:
admin.events.xxx = function(){ layer.msg($(this).text()); } <a ew-event="xxx">测试</a>
open和popupRight这两个事件是用来支持非js方式打开弹窗:
<button ew-event="open" data-type="2" data-content="http://baidu.com">iframe弹窗</button> <button ew-event="open" data-type="1" data-url="form.html">页面弹窗</button> <button ew-event="open" data-type="1" data-content="#userForm">页面弹窗</button> <form id="userForm">......省略</form> <!-- 设置area和offset --> <button ew-event="open" data-type="1" data-content="Hello" data-area="80px,60px" data-offset="10px,10px">页面弹窗</button> <!-- popupRight一样的用法 --> <button ew-event="popupRight" data-type="2" data-url="http://baidu.com" data-title="百度一下,你就知道">右侧弹窗</button> <!-- function类型参数写法,success、end等 --> <button ew-event="open" data-type="1" data-content="Hello" data-success="onDialogSuccess">页面弹窗</button> <script> layui.use(['layer'], function(){ var layer = layui.layer; // 方法需要加window window.onDialogSuccess = function(){ layer.msg('弹窗被成功打开了'); }; }); </script> <!-- 加data-window="top"相当于top.layui.admin.open --> <button ew-event="open" data-type="2" data-content="http://baidu.com" data-window="top">iframe弹窗</button>layer支持的参数大部分都可以通过data属性来设置,数组类型用逗号分隔,function类型需要把作用域放在window对象下, 也可以直接写js比如data-success="layer.msg('打开了弹窗')"。
通过admin.hasPerm(auth)可以控制按钮级别的权限隐藏:
if(!admin.hasPerm('user:add')) { $('#btnUserAdd').remove(); } hasPerm方法需要通过setter.getUserAuths方法来获取全部权限setter.getUserAuths是通过setter.getUser方法从用户信息中获取权限的setter.getUser是从本地缓存中获取用户信息的,setter.putUser把用户信息放入缓存中在main.js中有获取用户信息并通过setter.putUser放入缓存中的写法。
还可以通过加属性的方式来隐藏:
<button perm-show="user:add">添加</button>perm-show可以用于任意元素,对于动态添加的元素调用admin.renderPerm()来更新渲染,比如表格应该写在done回调里面:
table.render({ elem: '#userTable', url: '../../json/user.json', toolbar: ['<div>', '<button perm-show="user:add" lay-event="add" class="layui-btn">添加</button>', '<button perm-show="user:del" lay-event="del" class="layui-btn">删除</button>', '</div>'].join(''), cols: [[ {type: 'numbers'}, {field: 'username', title: '账号', sort: true}, {field: 'nickName', title: '用户名', sort: true}, {title: '操作', toolbar: '#userTbBar', align: 'center', minWidth: 200} ]], done: function() { admin.renderPerm(); } });
鼠标滑过弹出tips,使用示例:
<button lay-tips="大家好!">按钮</button> <button lay-tips="大家好!" lay-direction="2" lay-bg="#009788">按钮</button> <button lay-tips="大家好!" lay-offset="10px" lay-bgImg="linear-gradient(to right,#8510FF,#D025C2,#FF8B2D,#F64E2C)">按钮</button> <button lay-tips="大家好!" lay-offset="-10px,-10px" lay-padding="5px">按钮</button> lay-direction: 设置位置,1上面(默认)、2右边、3下面、4左边lay-bg: 设置背景颜色(包含箭头颜色,下面的背景图片不包含箭头)lay-offset: 设置偏移距离,(上,左)lay-padding:内间距lay-color:文字颜色lay-bgImg:背景图片,例如lay-bgImg="url(assets/images/head.jpg)"lay-fontSize:字体大小除了elem和text其他都是可选参数,还可以使用admin.tips()直接调用:
admin.tips({ elem: '#btn', text: 'Hello', direction: 3, bg: '#803ed9', offset: '-10px,-10px', padding: '15px', color: '#eee', bgImg: 'linear-gradient(to right,#8510FF,#D025C2,#FF8B2D,#F64E2C)', fontSize: '18px', tipsMore: true, time: -1 });
地图相关参数的详细介绍请前往高德地图API查看。
后面三个参数配置可参考upload模块, 如果想单独在页面中使用图片裁剪功能请参考Cropper插件文档
此方法会智能过滤除数字外的字符,如果动态了修改了数字应该加$('#xx').data('num', '');重置一下。
快速使用:
// GCJ02转BD09 var point = admin.util.Convert_GCJ02_To_BD09({lat: 30.505674, lng: 114.40043}); console.log(point.lng + ',' + point.lat); // BD09转GCJ02 var point = admin.util.Convert_BD09_To_GCJ02({lat: 30.512004, lng: 114.405701}); console.log(point.lng + ',' + point.lat); 高德地图、腾讯地图以及谷歌中国区地图使用的是GCJ-02坐标系百度地图使用的是BD-09坐标系不同的坐标系之间会有几十到几百米的偏移,所以在开发基于地图的产品时,可以通过此方法修正不同坐标系之间的偏差, 详细了解国内各坐标系。
对象型在参数传递时通常是引用传递,有时需要把对象克隆一份再传递,避免传递的参数被修改导致第二次传递出现问题。
判断规则,有文字,有img、audio、video、iframe等标签都属于不为空。
像上面这样有多个内联样式style,如何只移除其中一个或多个呢:
admin.util.removeStyle('background-color'); admin.util.removeStyle(['background-color', 'color']);
它与laytpl使用方式一致,并且解决了laytpl对js注释支持不友好,laytpl模板中有js注释会导致最后渲染的内容有问题。
加载独立页面:
<div id="demo1"></div> <script> layui.use(['layer', 'admin'], function () { var $ = layui.jquery; var admin = layui.admin; admin.ajax({ url: 'xxx.html', dataType: 'html', success: function (html) { $('#demo1').html(admin.util.tpl(html, {name: 'xxx'})); } }); }); </script>独立页面xxx.html:
<div>{{name}}</div> <script> layui.use(['layer'], function () { // 我可以加注释哦,laytpl不可以哦 layui.layer.msg('Hello 我被加载了'); }); </script>
由于spa版本多标签和单标签的滚动条所在元素都不一样,而且也不在body上, 所以封装了scrollTop方法可自动判断主体部分滚动条所在的元素。
监听侧边栏折叠/展开:
admin.on('flexible', function (d) { setTimeout(function () { myCharts1.resize(); }, 360); });一般用于更新echarts尺寸等,要延迟360毫秒再更新,因为折叠展开有个过渡效果。
监听页面被卸载:
admin.on('destroy(plugin/other/editor)', function () { tinymce.get('demoEditor').destroy(false); });括号里面是页面的路径地址,没有#号,可用于销毁定时器、组件等。
监听页面进入前台:
layui.admin.on('show(console/console)', function () { myCharts1.resize(); });括号里面是页面的路径地址,没有#号,一般用于更新echarts尺寸等,比如打开了两个标签页, 缩放屏幕大小再进入另一个标签页,echarts尺寸不会自动变化,可以监听页面进入前台并更新尺寸。
其他事件监听:
/* 监听tab切换事件 */ admin.on('tab', function (d) { // d.layId表示当前tab的url }); // 监听tab关闭事件 admin.on('tabDelete', function (d) { // d.layId表示当前tab的url }); /* 监听多系统切换事件 */ admin.on('nav', function (d) { // d.navId表示当前多系统的navId });实现刷新页面记忆折叠状态,在main.js增加:
admin.on('flexible', function (d) { admin.putTempData('nav-expand', d.expand); }); var expand = admin.getTempData('nav-expand'); if (expand !== undefined) admin.flexible(expand);
直接赋值数据:
<script type="text/html" ew-tpl="['a','b','c']" data-done="layui.form.render()"> <div> {{# layui.each(d,function(i,item){ }} <div>{{item}}</div> {{# }); }} </div> </script> <!-- 也可以引用变量 --> <script id="userListTpl" type="text/html" ew-tpl="userList"> <div> {{# if(d){ }} <!-- 这里判断以下因为可能值还没定义 --> {{# layui.each(d,function(i,item){ }} <div>{{item}}</div> {{# }); }} {{# } }} </div> </script> <script> layui.use(['admin'], function() { window.userList = ['a', 'b', 'c']; // admin模块一加载会自动渲染,但userList还未定义,所以定义完要重新渲染 admin.renderTpl('#userListTpl'); }); </script>使用远程数据,注意要加引号'user.json':
<script type="text/html" ew-tpl="'user.json'" data-method="post" data-where="{sex:'女'}" data-headers="{token: 'xxx'}"> <div> {{# layui.each(d.data,function(i,item){ }} <div>{{item.username}}</div> {{# }); }} </div> </script>通过ew-tpl来表示这是一个动态模板以及设置数据源,通过data-xxx配置其他参数:
done 渲染完成后的回调url 远程数据的urlmethod 远程数据的请求方式where 远程数据的请求条件,也可以data-where="JSON.stringify({})"headers 远程数据的请求headercontent-type 如data-content-type="application/json;charset=UTF-8"注意远程数据url一定要加引号,加引号的好处你还可以这样使用:
ew-tpl=" layui.setter.baseServer + '/user.json' "动态模板会在页面加载后自动渲染,如果要重新渲染:
admin.renderTpl('#userListTpl'); // 参数为模板的jquery选择器使用例子,比如用户管理的搜索里面的性别下拉换成动态的:
<!-- 表格工具栏 --> <form class="layui-form toolbar"> <div class="layui-form-item"> <div class="layui-inline"> <label class="layui-form-label">性 别:</label> <script type="text/html" ew-tpl="'../../json/sex.json'" data-done="layui.form.render('select');"> <div class="layui-input-inline"> <select name="sex"> <option value="">选择性别</option> {{# layui.each(d.data,function(i,item){ }} <option value="{{item.value}}">{{item.name}}</option> {{# }); }} </select> </div> </script> </div> </div> </form> <!-- 数据表格 --> <table id="userTable" lay-filter="userTable"></table>sex.json数据:
{"code": 200, "data": [{"name": "男", "value": 0}, {"name": "女", "value": 1}]}注意这里有个小细节,select上面还有一个div,对于select的动态模板,建议select外面最好多一层,因为select会被layui美化, 外面不加一层当重新渲染模板的时候不能把layui美化的旧的select移除,另外ew-tpl="'sex.json'"这里先是双引号再是单引号,不要漏掉了。
以上是easyweb增加的公共类,当然也可以使用Layui公共类。
表单弹窗示例:
<script type="text/html" id="modelUser"> <form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form"> <input name="userId" type="hidden"/> <div class="layui-form-item"> <label class="layui-form-label layui-form-required">账号</label> <div class="layui-input-block"> <input name="username" placeholder="请输入账号" class="layui-input"/> </div> </div> <div class="layui-form-item text-right"> <button class="layui-btn layui-btn-primary" type="button" ew-event="closePageDialog">取消</button> <button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button> </div> </form> </script> <script> layui.use(['admin'],function(){ var admin = layui.admin; admin.open({ type: 1, title: '添加用户', content: $('#modelUser').html(), success: function (layero, dIndex) { // 表单的操作,事件绑定等都写在success回调里面 } }); }); </script>固定底部操作按钮示例:
<script type="text/html" id="modelUser"> <form class="layui-form model-form no-padding" id="modelUserForm" lay-filter="modelUserForm"> <div class="model-form-body" style="max-height: 320px;"> <!-- 如果要超出屏幕才固定底部,不要写max-height --> <div class="layui-form-item"> <label class="layui-form-label">实习公司</label> <div class="layui-input-block"> <input name="companyName" class="layui-input"/> </div> </div> <!-- ......省略 --> </div> <div class="layui-form-item text-right model-form-footer"> <button class="layui-btn layui-btn-primary" type="button" ew-event="closePageDialog">取消</button> <button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button> </div> </form> </script>固定底部按钮需要model-form-body和model-form-footer,普通表单弹窗只需要model-form
移动端自动适配效果:
表格搜索折叠/展开事件监听:
<!-- 直接写js --> <a class="layui-btn" search-expand="d.expand?layer.msg('展开了'):layer.msg('折叠了')"> 展开 <i class="layui-icon layui-icon-down"></i> </a> <!-- 调用方法 --> <a class="layui-btn" search-expand="onExpandChange(d)"></a> <script> layui.use(['layer'], function () { window.onExpandChange = function(d){ console.log(d); // d包含d.expand,d.elem } }); </script>
只需要在layui-tab的基础上多加一个layui-tab-vertical类即可实现:
<div class="layui-tab layui-tab-vertical"> <ul class="layui-tab-title"> <li class="layui-this">系统管理</li> <li>用户管理</li> <li>权限分配</li> </ul> <div class="layui-tab-content"> <div class="layui-tab-item layui-show">内容1</div> <div class="layui-tab-item">内容2</div> <div class="layui-tab-item">内容3</div> </div> </div>如果要修改选项卡标题的宽度:
<!-- title和content两处地方都需要修改 --> <div class="layui-tab layui-tab-vertical"> <ul class="layui-tab-title" style="width: 200px;">......省略</ul> <div class="layui-tab-content" style="margin-left: 200px;">......省略</div> </div>如果要选项卡的标题在右侧,再多加一个layui-tab-vertical-right类即可:
<div class="layui-tab layui-tab-vertical layui-tab-vertical-right"> <ul class="layui-tab-title" style="width: 200px;">......省略</ul> <div class="layui-tab-content" style="margin-right: 200px;">......省略</div> </div>选项卡的灰色分隔线的高度是标题区域的高度,如果当内容高度高于标题区域高度时想要分隔线的高度最大:
<!-- 多加一个`layui-tab-vertical-full`类就可以了 --> <div class="layui-tab layui-tab-vertical layui-tab-vertical-full"> <ul class="layui-tab-title">......省略</ul> <div class="layui-tab-content">......省略</div> </div>垂直选项卡中使用栅格布局样式错位,在layui-row上加一些样式:
<div class="layui-tab layui-tab-vertical"> <ul class="layui-tab-title"> <li class="layui-this">系统管理</li> <li>用户管理</li> </ul> <div class="layui-tab-content"> <div class="layui-tab-item layui-show">内容1</div> <div class="layui-tab-item"> <div class="layui-row" style="float: left;width: 100%;"> <div class="layui-col-md6"> 内容2 </div> <div class="layui-col-md6"> 内容使用栅格 </div> </div> </div> </div> </div>
用layui-badge-list包裹,会有更好的间距和尺寸:
<div class="layui-badge-list"> <span class="layui-badge layui-badge-gray">很有想法的</span> <span class="layui-badge layui-badge-gray">专注设计</span> <span class="layui-badge layui-badge-gray">辣~</span> <span class="layui-badge layui-badge-gray">大长腿</span> <span class="layui-badge layui-badge-gray">川妹子</span> <span class="layui-badge layui-badge-gray">海纳百川</span> </div>
xmSelect下拉单选树增加一个ew-xmselect-tree可以有更好的样式。
只需要在select的父元素上面增加class ew-select-fixed 即可:
<div style="height: 80px;overflow: auto;" onscroll="layui.admin.hideFixedEl();"> <div class="ew-select-fixed"> <select name="ptTypeId"> <option value="">请选择</option> <option value="1">选项一</option> <option value="2">选项二</option> </select> </div> </div>fixed定位的select用在数据表格中不需要对表格做任何修改就可以显示出来,还可以用在带有滚动表的容器中,比如一个很长的表单弹窗, 一定高度后显示滚动条,里面select是无法越出滚动条容器显示的,使用fixed方式的select即可超出容器的滚动条显示, 使用了fixed定位后外层容器滚动select不会跟随滚动,可在容器上添加滚动时收起select。
遮罩层是分离式的绑定,通过data-dropdown绑定:
<button class="layui-btn layui-btn-normal icon-btn" data-dropdown="#dropdown1"> 带遮罩层 <i class="layui-icon layui-icon-drop"></i> </button> <!-- 下拉菜单 --> <ul class="dropdown-menu-nav dropdown-bottom-right layui-hide" id="dropdown1"> <div class="dropdown-anchor"></div> <li class="title">HEADER</li> <li><a><i class="layui-icon layui-icon-star-fill"></i>1st menu item</a></li> <li class="disabled"><a><i class="layui-icon layui-icon-template-1"></i>2nd menu item</a></li> <hr> <li class="title">HEADER</li> <li><a><i class="layui-icon layui-icon-set-fill"></i>3rd menu item</a></li> </ul>
在dropdown-menu-nav上加下面的class可控制位置:
类名位置dropdown-bottom-left下左弹出dropdown-bottom-center下中弹出dropdown-bottom-right下右弹出 dropdown-top-left上左弹出dropdown-top-center上中弹出dropdown-top-right上右弹出 dropdown-left-top左上弹出dropdown-left-center左中弹出dropdown-left-bottom左下弹出 dropdown-right-top右上弹出dropdown-right-center右中弹出dropdown-right-bottom右下弹出 <!-- Bottom Center --> <div class="dropdown-menu dropdown-hover"> <button class="layui-btn layui-btn-primary icon-btn">Bottom <i class="layui-icon layui-icon-drop"></i> Center </button> <ul class="dropdown-menu-nav dropdown-bottom-center"><!-- 这里加控制方向的类 --> <div class="dropdown-anchor"></div> <li><a>1st menu item</a></li> <li><a>2nd menu item</a></li> <li><a>3rd menu item</a></li> </ul> </div> <!-- Bottom Right --> <div class="dropdown-menu dropdown-hover"> <button class="layui-btn layui-btn-primary icon-btn">Bottom Right <i class="layui-icon layui-icon-drop"></i> </button> <ul class="dropdown-menu-nav dropdown-bottom-right"><!-- 这里加控制方向的类 --> <div class="dropdown-anchor"></div> <li><a>1st menu item</a></li> <li><a>2nd menu item</a></li> <li><a>3rd menu item</a></li> </ul> </div>
数据表格中使用分离式的绑定,并且加no-shade="true"去掉遮罩层:
<!-- 表格操作列 --> <script type="text/html" id="tableBar"> <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a> <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a> <!-- 下拉菜单 --> <a class="layui-btn layui-btn-xs" data-dropdown="#dropUser{{d.LAY_INDEX}}" no-shade="true"> 更多<i class="layui-icon layui-icon-drop" style="font-size: 12px;margin-right: 0;"></i></a> <ul class="dropdown-menu-nav dropdown-bottom-right layui-hide" id="dropUser{{d.LAY_INDEX}}"> <div class="dropdown-anchor"></div> <li><a lay-event="edit2"><i class="layui-icon layui-icon-edit"></i>修改用户</a></li> <li><a lay-event="del2"><i class="layui-icon layui-icon-delete"></i>删除用户</a></li> <li><a lay-event="lock2"><i class="layui-icon layui-icon-password"></i>锁定用户</a></li> </ul> </script> <script> layui.use(['table', 'dropdown'], function () { var table = layui.table; table.render({ cols: [[ {field: 'username', title: '账号'}, {toolbar: '#tableBar', title: '操作'} ]] }); }); </script>
统一设置默认值:
notice.settings({ timeout: 1500, transitionIn: 'flipInX', onOpened: function(){ console.log('notice opend!'); } });关闭指定的通知:
notice.hide({}, document.querySelector('.toast')); 参数一 重写一些参数,比如关闭动画等参数二 根据自定义的className选择关闭的对象
参数position显示位置可选值:
属性说明bottomRight右下角bottomLeft左下角topRight右上角topLeft左上角topCenter顶部中间bottomCenter底部中间center正中间参数transitionIn进入动画可选值:
属性说明bounceInLeft向左反弹bounceInRight向右反弹bounceInUp向上反弹bounceInDown向下反弹fadeIn淡入fadeInDown向下淡入fadeInUp向上淡入fadeInLeft向左淡入fadeInRight向右淡入flipInX翻转进入参数transitionOut退出动画可选值:
属性说明fadeOut淡出fadeOutUp向上淡出fadeOutDown向下淡出fadeOutLeft向左淡出fadeOutRight向右淡出flipOutX翻转退出
异步加载的数据格式为:
[ {value: 'beijing', label: '北京', haveChildren: true}, {value: 'jiangsu', label: '江苏', haveChildren: true} ]通过haveChildren字段来标识是否还有子节点,如果你的后台数据格式不是这样,可以在callback之前格式化:
cascader.render({ elem: '#demoCascader1', reqData: function (values, callback, data) { // values是当前所有选中的值,data是当前选中的对象 $.get('xxxx.json', { id: data.value }, function(res){ var newList = []; for(var i=0;i<res.data.length;i++){ var item = res.data[i]; newList.push({ value: item.id, label: item.name, haveChildren: item.haveChildren }); } callback(newList); // 数据请求完成通过callback回调 },'json'); } });
搜索后端接口返回的数据格式为:
[ {"value": "1,2", "label": "北京 / 王府井"}, {"value": "1,3", "label": "北京 / 故宫"} ]如果要标记关键字为红色:<span class=\"search-keyword\">北京</span> / 王府井
省市区的数据已经封装好了,只需要引入数据的js即可,数据包含省市区,value是区号, 如果需要对数据进行处理,cascader封装了几种处理数据方法:
// value也是中文而不是区号 var data = cascader.getCityData(admin.util.deepClone(citysData)); cascader.render({ elem: '#demoCascader11', data: data }); // 只显示省市,不要区域 var data = cascader.getCity(admin.util.deepClone(citysData)); // 只显示省份,不要市区 var data = cascader.getProvince(admin.util.deepClone(citysData)); // 只显示身份,并且value是中文,而不是区号 var data = cascader.getCityData(cascader.getProvince(admin.util.deepClone(citysData)));
autocomplete参数:
$('#demoTagsInput').tagsInput({ autocomplete_url: '../../json/tagsInput.json', autocomplete: { type: 'post', data: { access_token: 'xxxxx' } } });type是请求方式,默认是get请求,data是额外参数,请求autocomplete_url会传递name参数(输入框的值)。
更详细的使用文档可以参考jQuery-Tags-Input。
此插件基于 jQuery-Tags-Input 二次修改。
文字样式:
vertical 垂直percent 百分比value 只显示值valueOnCircle 值显示在进度条上none 不显示文字自定义文字样式:
new CircleProgress('#demoProgress9', { max: 12, value: 9, textFormat: function (value, max) { return value + ' dots'; } });
使用css自定义样式:
<div id="demoProgress1"></div> <script> layui.use(['CircleProgress'], function () { var CircleProgress = layui.CircleProgress; // 快速使用 new CircleProgress('#demoProgress1', { max: 100, value: 20, textFormat: 'percent' }); }); </script> <style> /* 进度条选中的样式 */ #demoProgress1 .circle-progress-value { stroke-width: 8px; /* 粗度 */ stroke: #3FDABA; /* 颜色 */ } /* 进度条未选中的样式 */ #demoProgress1 .circle-progress-circle { stroke-width: 6px; stroke: #E0FAF1; } </style>
此插件是使用svg实现的,想要实现更丰富的样式,可先了解下svg
默认样式:
<div class="layui-tab layui-steps"> <ul class="layui-tab-title"> <li> <i class="layui-icon layui-icon-ok">1</i> <span class="layui-steps-title">已完成</span> <span class="layui-steps-content">这里是该步骤的描述信息</span> </li> <li class="layui-this"> <i class="layui-icon layui-icon-ok">2</i> <span class="layui-steps-title">进行中</span> <span class="layui-steps-content">这里是该步骤的描述信息</span> </li> <li> <i class="layui-icon layui-icon-ok">3</i> <span class="layui-steps-title">待进行</span> <span class="layui-steps-content">这里是该步骤的描述信息</span> </li> </ul> <div class="layui-tab-content"> <div class="layui-tab-item">内容1</div> <div class="layui-tab-item layui-show">内容2</div> <div class="layui-tab-item">内容3</div> </div> </div> <script> layui.use(['steps'], function () { var steps = layui.steps; }); </script>它的结构与layui的选项卡Tab一致,因为它就是在Tab的基础上增加的样式。
迷你样式:
<!-- 多加一个layui-steps-small即可 --> <div class="layui-tab layui-steps layui-steps-small"> <!-- 省略...... --> </div>自定义图标:
<div class="layui-tab layui-steps layui-steps-small"> <ul class="layui-tab-title"> <li class="layui-this"> <!-- 这里的图标可以任意修改 --> <i class="layui-icon layui-icon-username"></i> <span class="layui-steps-title">账号注册</span> </li> <li> <i class="layui-icon layui-icon-camera"></i> <span class="layui-steps-title">上传头像</span> </li> </ul> <div class="layui-tab-content"> <div class="layui-tab-item layui-show">内容1</div> <div class="layui-tab-item">内容2</div> </div> </div>垂直风格:
<!-- 多加一个layui-steps-vertical即可 --> <div class="layui-tab layui-steps layui-steps-vertical"> <!-- 省略...... --> </div>简洁风格:
<!-- 多加一个layui-steps-simple --> <div class="layui-tab layui-steps layui-steps-simple"> <ul class="layui-tab-title"> <li>1.填写注册手机号</li> <li class="layui-this">2.获取短信验证码</li> <li>3.修改登录密码</li> </ul> <div class="layui-tab-content"> <div class="layui-tab-item">内容1</div> <div class="layui-tab-item layui-show">内容2</div> <div class="layui-tab-item">内容3</div> </div> </div>注意 简洁风格只有标题,没有图标、描述等
也可以使用属性绑定:
<div class="layui-tab layui-steps"> <!-- 省略...... --> <a class="layui-btn" data-steps="prev">上一步</a> <a class="layui-btn" data-steps="next">下一步</a> <a class="layui-btn" data-steps="go" data-go="1">跳转第2步</a> </div>
在table.render的done回调里面使用tableX.merges方法:
table.render({ elem: '#xTable2', cols: [[ {type: 'numbers'}, {field: 'parentName', title: '模块名称', sort: true}, {field: 'authorityName', title: '菜单名称', sort: true} ]], done: function () { tableX.merges('xTable2', [1]); } });参数说明:
tableX.merges('xTable2', [1]); // 合并第2列相同的单元格 tableX.merges('xTable2', [1], ['parentName']); // 合并第2列相同的单元格 tableX.merges('xTable2', [1, 2]); // 合并第2、3列相同的单元格 tableX.merges('xTable2', [1, 2], ['parentName', 'authorityName']); // 合并第2、3列相同的单元格 tableX.merges('xTable2', [1, 2], false); // 在后面加一个false可解决排序冲突的问题 参数一 必填 表格的lay-filter参数二 必填 要合并列的索引,数组类型参数三 非必填 根据数据字段判断是否相同,为空时根据单元格的html内容判断参数四 非必填 是否需要监听排序后重新合并,默认为true
在table.render的done回调里面使用tableX.bindCtxMenu方法:
table.render({ elem: '#xTable3', cols: [[ {field: 'nickName', title: '用户名'}, {field: 'sex', title: '性别'} ]], done: function () { tableX.bindCtxMenu('xTable3', [{ icon: 'layui-icon layui-icon-edit', name: '修改此用户', click: function (d, tr) { layer.msg('点击了修改,userId:' + d.userId); } }, { icon: 'layui-icon layui-icon-close text-danger', name: '<span class="text-danger">删除此用户</span>', subs: [{ icon: 'layui-icon layui-icon-camera', name: '逻辑删除', click: function (d) { layer.msg('点击了逻辑删除,userId:' + d.userId); } }, { icon: 'layui-icon layui-icon-picture-fine', name: '物理删除', click: function (d) { layer.msg('点击了物理删除,userId:' + d.userId); } }] }]); } }); 参数一 表格的id参数二 右键菜单 icon 图标name 标题click 点击事件,d是当前行的数据根据不同行动态显示不同菜单,只需要把参数二换成function并return菜单数组即可:
tableX.bindCtxMenu('xTable3', function(d, tr) { if(d.userId === 1) { return [{ icon: 'layui-icon layui-icon-close text-danger', name: '<span class="text-danger">删除此用户</span>', click: function (d, tr) { layer.msg('点击了删除,userId:' + d.userId); } }]; } else { return [{ icon: 'layui-icon layui-icon-edit', name: '修改此用户', click: function (d, tr) { layer.msg('点击了修改,userId:' + d.userId); } }]; } });
仅仅是把table.render换成tableX.render就会在点击排序时自动传递sort和order参数,例如:user?page=1&limit=10&sort=sex&order=asc。
sort 排序字段,值是点击排序列的fieldorder 排序方式,升序是asc,降序是desc注意:如果写了sort: true开启排序,一定要写field: 'xxx'
仅仅是把table.render换成tableX.renderFront就可以有前端分页和排序功能了,参数跟layui表格的参数一样,url方式你的接口可以返回全部数据,前端来分页,也支持data方式。
前端模糊搜索:
<input tb-search="xTable1" class="layui-input icon-search"/>在页面任意位置加入上面输入框,通过tb-search关联表格,就实现了对表格的模糊搜索功能。 还可以增加name属性来设置搜索时只搜索某些字段,多个字段通过逗号分隔:
<input tb-search="xTable1" name="sex,phone" class="layui-input icon-search"/>刷新功能:
<button tb-refresh="xTable1" class="layui-btn">刷新</button>在页面任意位置加入上面按钮,通过tb-refresh关联表格。 也可以通过js刷新:
// url方式渲染的只能用此方法刷新 var insTb = tableX.renderFront({url: 'xxx'}); insTb.reloadUrl(); // data方式渲染的只能用此方法刷新 var insTb = tableX.renderFront({data: []}); insTb.reloadData({data: dataList, page: {curr: 1}});前端排序:
前端排序如果有field字段,会根据field字段的值来排序,如果有templet会根据templet转换后的值排序, templet可能会返回表单元素,比如switch开关等,这些元素无法用来排序,可以通过export-show和export-hide来控制。
<!-- switch开关列 --> <script type="text/html" id="tableState"> <input type="checkbox" lay-skin="switch" lay-text="正常|锁定" lay-filter="ckState" value="{{d.userId}}" {{d.state==0?'checked':''}}/> <div class="export-show">{{d.state==0?'正常':'锁定'}}</div> </script> <!-- icon图标列 --> <script type="text/html" id="tableState"> <div class="export-hide">{{d.state==0?'<i class="layui-icon layui-icon-ok"></i>':'<i class="layui-icon layui-icon-close"></i>'}}</div> <div class="export-show">{{d.state==0?'正常':'锁定'}}</div> </script> export-show 排序和导出时有效,表格展示时屏蔽export-hide 表格展示时有效,排序和导出时屏蔽
如果data是string类型会把data当url请求数据,option是请求的配置,跟表格的配置一样,配置method、where、headers等, 接口返回的格式也要跟表格一样包含code、count、data等信息。
cols的配置也跟表格一样,是一个多维数组,可以通过insTb3.config.cols来获取表格的cols,也可以重写。
导出的数据会包含templet转换,如果templet返回的是switch开关、icon图标等,可以通过export-show和export-hide写两份, 一份用于表格的展示,一份用于表格的导出显示,具体用法参考前面前端排序章节的介绍。
cols也可以重写:
tableX.exportData({ cols: [[ {field: 'username', title: '账号'}, {field: 'nickName', title: '用户名'} ]], data: table.cache.xTable3, fileName: '用户表' });exportDataX方法使用的是社区excel模块导出的,是真正的xls格式,exportData以及layui自带的导出都是假的xls格式,如果数字前面有0会自动去掉0,exportDataX可解决此问题。
后端分页时如何导出全部数据:
tableX.exportDataX({ cols: insTb.config.cols, data: 'listAll.json', fileName: '用户表' }); // 后端再写一个查询全部的接口,data写接口地址导出搜索后的全部数据:
var lastWhere; // 记录搜索的条件 // 表格搜索 form.on('submit(formSubSearchUser)', function (data) { lastWhere = data.field; insTb.reload({where: data.field}, 'data'); }); tableX.exportDataX({ cols: insTb.config.cols, data: 'listAll.json', option: { where: lastWhere }, fileName: '用户表' });在options里面加一个where把搜索条件传给后端。
后端导出本质就是下载文件,一般用window.open(url)即可,如果要传递参数、post提交,那就麻烦了,tableX进行了完美的封装:
tableX.exportDataBack('user/export', {sex: '男'}); tableX.exportDataBack('user/export', {sex: '男'}, 'post'); 参数一 后端的url参数二 传递的参数参数三 请求方式如果是get方式,会使用?和&拼接参数,如果是post方式,会创建一个隐藏的表单来提交, 如果你的参数有复杂的类型,比如json格式,建议用post的形式,此方法也可以用来做post方式下载文件的操作。
使用示例:
<form class="layui-form"> <input class="layui-input" placeholder="请输入手机号" lay-verType="tips" lay-verify="phoneX"/> <input class="layui-input" placeholder="请输入手机号" lay-verType="tips" lay-verify="required|phoneX"/> <input class="layui-input" placeholder="请输入整数" lay-verType="tips" lay-verify="digits"/> <input class="layui-input" placeholder="请输入正整数" lay-verType="tips" lay-verify="digitsP"/> </form> <script> layui.use(['formX'],function(){ var formX = layui.formX; // 要引入formX模块才会生效 }); </script>equalTo用法,可以用来验证两次输入是否一致:
<form class="layui-form"> <input id="demoPsw" class="layui-input" placeholder="请输入密码" lay-verType="tips" lay-verify="required|psw"/> <input class="layui-input" placeholder="请再次输入密码" lay-verType="tips" lay-verify="equalTo" lay-equalTo="#demoPsw" lay-equalToText="两次输入密码不一致"/> </form> 属性描述lay-equalTo关联输入框的dom选择器lay-equalToText自定义提示文本h5用法:
属性描述minlength最少输入字符长度maxlength最多输入字符长度min最小输入数值max最大输入数值 <form class="layui-form"> <input class="layui-input" placeholder="最少输入5个字符" minlength="5" lay-verType="tips" lay-verify="required|h5"/> <input class="layui-input" placeholder="最多输入10个字符" maxlength="10" lay-verType="tips" lay-verify="h5"/> <input class="layui-input" type="number" placeholder="值只能在-9到9之间" min="-9" max="9" lay-verType="tips" lay-verify="required|numberX|h5"/> </form>phoneX、emailX等与layui自带phone、email等的区别是如果没有输入不会验证,输入了才验证格式。
使用方法:
layui.use(['formX'],function(){ var formX = layui.formX; // 赋值表单,支持top.formX.val()用法 formX.val('userForm', {name: 'user01'}); });
基础参数:
elem 要渲染的selectdata 数据源,可以是数组,也可以是urlname 显示的字段名称value 值的字段名称hint 未选择提示文字initValue 默认回显的数据done 渲染完成后的回调error data为url时请求失败的回调method data为url时请求方式where data为url时请求参数header data为url时请求headerasync data为url时请求时否是异步后端返回数据格式为{"data": [], "code": 0},data有数据就是请求成功,没有数据就进入error,code等于多少都可以, 如果数据不是对象的形式,name和value可不填:
formX.renderSelect({ elem: '#sel', data: ['张三','李四'] });
利用这个方法可以做判断用户是否修改了表单:
var user = {sex: 'male', age: 18}; form.val('userForm', user); // 表单回显数据 // 监听表单提交 form.on('submit(submit-user)', function (data) { var u = formX.formUpdatedField(data.field, user); if(!u) { alert('你没有做任何修改'); } return false; });
参数都是可以选参数,默认值已经符合大多数需求。
加hide-print这个class即可,可以跟hide参数叠加使用。
打印表格时可以给table加print-table这个class来设置表格的边框样式:<table class="print-table">。
如果print为false关闭自动打印,可以使用pWindow.print()触发打印。
参数都是可选参数:
htmls 数组,代表每一页;style 样式,也可以写<link rel="stylesheet">的形式;padding 页面间距,例如写'10px';debug 调试模式,每一页会加红色边框,便于调试大小;
菜单数组可支持无限极:
[{ icon: 'xxxxx', name: '菜单三', subs: [{ icon: 'xxxxx', name: '子菜单一', click: function (e, event) { alert('点击了子菜单一'); } }] }] icon 图标name 菜单名click 点击事件subs 子菜单(支持无限极)click事件里面的e是右键菜单的事件对象,event才是绑定的目标元素的事件对象
例如用于点击事件:
$('#btn').click(function (e) { var x = $(this).offset().left; var y = $(this).offset().top + $(this).outerHeight() - $('html,body').scrollTop(); contextMenu.show([{ name: '按钮菜单一', click: function () { } }], x, y, e); e.preventDefault(); e.stopPropagation(); });你可以自己绑定事件,通过show方法显示出来,参数说明:
参数一 菜单数组参数二 x坐标参数三 y坐标参数四 e 原始事件对象
对于动态生成的元素可以使用事件委托的方式来绑定:
// 对.btn元素绑定鼠标右键 $ (document). bind('contextmenu', '.btn', function (e) { contextMenu.show([{ icon: 'layui-icon layui-icon-set', name: '删除', click: function (e, event) { layer.msg('点击了刪除'); } }],e. clientX, e. clientY, e); return false; });
后台管理页面大多用数据表格展示,如果要使用网格、瀑布流、卡片列表等形式,分页、自动渲染、搜索、排序等功能都需要自己实现, 极其麻烦,dataGrid模块就是对非表格形式的列表页面实现类似数据表格的功能,也在使用方法上与数据表格大致相同。
<div id="demoGrid"></div><!-- 容器 --> <!-- 模板 --> <script type="text/html" id="demoGridItem"> <div> <h2>{{d.title}}</h2> <p>{{d.content}}</p> <a lay-event="add">添加</a> <a lay-event="edit">修改</a> </div> </script> <script> layui.use(['dataGrid'], function () { var dataGrid = layui.dataGrid; /* 渲染 */ var ins = dataGrid.render({ elem: '#demoGrid', // 容器 templet: '#demoGridItem', // 模板 url: 'json/list.json', // 数据接口 page: {limit: 5} // 开启分页 }); /* item点击事件 */ dataGrid.on('item(demoGrid)', function (obj) { obj.data; // 当前操作的数据对象 obj.elem; // 当前操作的dom元素 obj.del(); // 删除当前item obj.update({title: 'new title'}); // 修改当前item layer.msg('点击了第' + (obj.index + 1) + '个'); }); /* item里面的lay-event的点击事件 */ dataGrid.on('tool(demoGrid)', function (obj) { var data = obj.data; if (obj.event === 'add') { layer.msg('点击了添加'); } else if (obj.event === 'edit') { layer.msg('点击了编辑'); } }); /* item双击事件 */ dataGrid.on('itemDouble(demoGrid)', function (obj) { }); /* 监听复选框选择 */ dataGrid.on('checkbox(demoGrid)', function (obj) { console.log(obj.checked); // 当前是否选中状态 console.log(obj.data); // 选中行的相关数据 console.log(obj.type); // 如果触发的是全选,则为all }); }); </script>
parseData和request参数说明:
var ins = dataGrid.render({ elem: '#demoGrid', templet: '#demoGridItem', url: 'json/list.json', parseData: function(res) { return { code: res.status, msg: res.message, count: res.total, data: res.list } }, request: {pageName: 'page', limitName: 'limit'} });渲染完成的回调done说明:
dataGrid.render({ done: function(dataList, curr, count) { dataList; // 当前页数据 curr; // 当前第几页 count; // 总数量 } });
使用page参数开启分页,page参数同layui分页组件的参数一致前往查看。
dataGrid.render({ elem: '#demoGrid', // 容器 templet: '#demoGridItem', // 模板 url: 'json/list.json', // 数据接口 page: {limit: 5} // 开启分页 });
loadMore和page只能二选一,loadMore的可选参数:
参数说明默认值class自定义class''limit每页多少条10text显示文字'加载更多'loadingText加载中文字'加载中...'noMoreText无数据文字'没有更多数据了~'errorText加载失败文字'加载失败,请重试'
通过添加data-grid和data-grid-tpl属性,dataGrid组件便会自动渲染,通过lay-data属性进行参数配置。
菜单配置及点击事件:
fileChoose.open({ menu: [{ name: '预览', event: 'preview' }, { name: '复制', event: 'copy' }, { name: '<span style="color: red;">删除</span>', event: 'del' }], menuClick: function(event, item) { // event 事件名称 // item 当前数据 } });name菜单项名称,event点击事件名称
接口数据格式化:
fileChoose.open({ response: { method: 'get', // 请求方式 code: 0, // 成功码,默认200 name: 'name', // 文件名称字段名称 url: 'url', // 文件url字段名称 smUrl: 'smUrl', // 文件缩略图字段名称 isDir: 'isDir', // 是否是文件夹字段名称,boolean类型 dir: 'dir' // 当前文件夹参数名称 } });接口数据返回的格式需要为:
{ "code": 200, "msg": "请求成功", "data": [ { "name": "图片一", "url": "2019/07/11/001.png", "smUrl": "sm/2019/07/11/001.png", "isDir": false } ] }code、msg、data是必须按这个名字的,name、url、smUrl、isDir这几个字段的名称可以通过response参数配置,也可以加其他字段, 比如id、create_time等,这些字段会在菜单点击事件和选择回调事件中返回。
如果你的接口返回的数据不是code、msg,是其他的,比如status、message,可以使用parseData参数格式化:
fileChoose.open({ response: { parseData: function(res){ return { code: res.status, msg: res.message, data: res.list } } } });如果是文件夹,点击文件夹会重新请求接口,并且传递文件夹的名称,传递的字段名称可以通过response.dir修改。
上传文件上传成功后默认会进入到当前日期生成的dir下面,你可以后端上传成功的接口返回dir指定上传成功后要进入的dir, 也可以把上传前端传递的dir原封不动的返回:
{"code": 200,"msg": "上传成功", "dir": "/2020/0516"}不同文件显示不同的图标是前端根据文件url的后缀名称来判断的,在之前版本是服务器根据文件的content-type判断的。
此插件来源于 jquery-mousewheel,使用方式:
layui.use(['mousewheel'], function () { var $ = layui.jquery; // 滚动监听 $('#xxx').on('mousewheel', function (event) { console.log(event.deltaX, event.deltaY, event.deltaFactor); event.stopPropagation(); // 阻止事件冒泡 event.preventDefault(); // 阻止默认事件 }); });event对象中可以获取如下三个属性值:
deltaX:值为负的(-1)表示滚轮向左滚动,值为正的(1)表示滚轮向右滚动。deltaY:值为负的(-1)表示滚轮向下滚动。值为正的(1)表示滚轮向上滚动。deltaFactor:增量因子,通过deltaFactor*deltaX或者deltaFactor*deltaY可以得到浏览器实际的滚动距离。
此插件来源于 qrcodejs,使用方式:
<div id="xxx"></div> <script> layui.use(['QRCode'], function () { var $ = layui.jquery; var QRCode = layui.QRCode; // 二维码 var demoQrCode = new QRCode(document.getElementById("xxx"), { text: "Hello Word!", width: 101, // 宽度 height: 101, // 高度 colorDark: "#000000", // 颜色 colorLight: "#ffffff", // 背景颜色 correctLevel: QRCode.CorrectLevel.H }); // 更换内容 demoQrCode.makeCode("Easyweb"); }); </script>
此插件来源于 intro.js,并对样式进行了微调,使用方式:
<div data-step="1" data-intro="这是步骤一">步骤一</div> <div data-step="2" data-intro="这是步骤二">步骤二</div> <script> layui.use(['introJs'], function () { var introJs = layui.introJs; // 初始化 introJs().start(); }); </script>
此插件来源于 clipboard.js,使用方式:
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git"> <button id="btnCopy" data-clipboard-target="#foo">复制</button> <script> layui.use(['ClipboardJS'], function () { var ClipboardJS = layui.ClipboardJS; var clipboard = new ClipboardJS('#btnCopy'); clipboard.on('success', function(e) { e.clearSelection(); }); clipboard.on('error', function(e) { console.error('Action:', e.action); }); }); </script>按钮通过data-clipboard-target绑定的不一定是input,div也可以,也不一定用id,jquery选择器都可以。
视频播放器使用的是西瓜视频开源的xgplayer,使用方式:
<div id="demoVideo"></div> <script> layui.use(['Player'], function () { var Player = layui.Player; // 视频播放器 var player = new Player({ id: "demoVideo", url: "//s1.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo.mp4", // 视频地址 poster: "https://imgcache.qq.com/open_proj/proj_qcloud_v2/gateway/solution/general-video/css/img/scene/1.png", // 封面 fluid: true, // 宽度100% playbackRate: [0.5, 1, 1.5, 2], // 开启倍速播放 pip: true, // 开启画中画 lang: 'zh-cn' }); // 开启弹幕 var dmStyle = { color: '#ffcd08', fontSize: '20px' }; var player = new Player({ id: "demoVideo2", url: "http://demo.htmleaf.com/1704/201704071459/video/2.mp4", // 视频地址 autoplay: false, fluid: true, // 宽度100% lang: 'zh-cn', danmu: { comments: [ {id: '1', start: 0, txt: '空降', color: true, style: dmStyle, duration: 15000}, {id: '2', start: 1500, txt: '前方高能', color: true, style: dmStyle, duration: 15000}, {id: '3', start: 3500, txt: '弹幕护体', color: true, style: dmStyle, duration: 15000}, ] } }); }); </script>
富文本编辑器使用的是TinyMCE,查看中文文档。
<textarea id="demoEditor"></textarea> <script type="text/javascript" src="assets/libs/tinymce/tinymce.min.js"></script> <script> layui.use(['layer'], function () { var $ = layui.jquery; var layer = layui.layer; // 渲染富文本编辑器 tinymce.init({ selector: '#demoEditor', height: 525, branding: false, language: 'zh_CN', plugins: 'code print preview fullscreen paste searchreplace save autosave link autolink image imagetools media table codesample lists advlist hr charmap emoticons anchor directionality pagebreak quickbars nonbreaking visualblocks visualchars wordcount', toolbar: 'fullscreen preview code | undo redo | forecolor backcolor | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | formatselect fontselect fontsizeselect | link image media emoticons charmap anchor pagebreak codesample | ltr rtl', toolbar_drawer: 'sliding', images_upload_url: '../../../json/tinymce-upload-ok.json', file_picker_types: 'media', file_picker_callback: function (callback, value, meta) { layer.msg('演示环境不允许上传', {anim: 6}); }, init_instance_callback: function (editor) { console.log(editor); // 编辑器渲染完成回调,回显值应该在这里回显 // tinymce.get('demoEditor').setContent('<span>Hello</span>'); } }); // 获取内容 var content = tinymce.get('demoEditor').getContent(); // 获取纯文本 var content = tinymce.get('demoEditor').getContent({format: 'text'}); // 设置内容 tinymce.get('demoEditor').setContent('<span>Hello</span>'); // 插入内容 tinymce.get('demoEditor').insertContent('👍赞~', {}); }); </script>弹窗中使用应该在弹窗关闭后销毁编辑器实例:
admin.open({ type: 1, content: '<textarea id="demoEditor2"></textarea>', success: function () { // 渲染编辑器 tinymce.init({ selector: '#demoEditor2' }); }, end: function () { // 销毁编辑器 tinymce.get('demoEditor2').destroy(false); } });解决弹窗遮挡富文本组件:
<!-- 页面加入样式 --> <style> body .tox-tinymce-aux { z-index: 19892000 !important; } </style>单页面中使用应该在页面卸载后销毁编辑器实例:
// 渲染编辑器 tinymce.init({ selector: '#demoEditor' }); // 监听页面卸载并销毁编辑器 admin.on('destroy(plugin/other/editor)', function () { tinymce.get('demoEditor').destroy(false); });如果你多个页面都用到了TinyMCE,建议js的引用放在index.html中:
<script type="text/javascript" src="assets/libs/tinymce/tinymce.min.js"></script>
默认的样式以及内置的几套主题都在admin.css中,如果要自定义主题步骤如下:
前往 主题生成器 在线定制主题;将生成的css放在admin.css里面,也可以像admin.css一样引入,可以直接通过<style>写在页面中,也可以建一个theme-all.css引入, 将所有生成的主题都放在这个css文件里面;打开“page/tpl/tpl-theme.html”添加生成的主题: <div class="more-theme-list"> <!-- data-theme写主题的名称 --> <div class="more-theme-item" data-theme="theme-cyan"> <img src="assets/module/img/theme-cyan.png"/> </div> </div>主题的预览图在主题生成器中会生成,右键可保存为图片,theme-all.css最好在admin.css之后引入。
设置默认主题也会闪一下才会切换怎么办:
因为主题切换是通过js切换的,js都是在css、html加在完毕之后执行的,有一点点延迟也很正常,而且换主题是换class, 大部分组件都写有transition的0.3s的css过渡效果,解决办法只能是直接在index.html的body上面加上主题对应的class:
<body class="theme-red" data-theme="theme-red"></body>或者直接修改下主题的css,把前面的.theme-xxx改成body,这种方法只适合固定一套主题不需要切换的情况。
集成社区模块:
把下载好的模块放在/assets/module/下面,如果模块是一个js,不用做任何配置就可以使用了, 例如admin.js、index.js,如果模块是一个文件夹,还需要在main.js中配置模块的具体位置,如notice/notice。
自定义扩展模块:
如果需要自己写一个扩展模块,请前往阅读Layui定义模块的开发文档, 或者参考admin.js、index.js等模块。
集成jquery插件:
jquery作为老牌框架,有着丰富的第三方插件,把插件放入libs下,页面中先引入jquery,再引入插件,即可使用。 当然更友好的集成方式是把它改造成layui扩展模块,改造方式请参考layui 封装第三方组件。
SPA单页面的缺点已经很明显了,就是ID不能重复(即使是多个页面),这个缺点只能通过规范ID命名来解决,或者关闭多标签模式, 或者标签全部用iframe。
在3.1.6版本后menu.json新支持了iframe字段:
{ "name": "用户管理", "url": "#/system/user", "iframe": "http://baidu.com" }当浏览器访问#/system/user时,框架默认是通过ajax加载components/system/user.html的内容拼接到index.html中, 如果有iframe字段就是把<iframe src="http://baidu.com">拼接到index.html中,所以如果把菜单这样配置,就相当于iframe版的形式:
{ "name": "用户管理", "url": "#/system/user", "iframe": "components/system/user.html" }这样配置后user.html就不再是一个代码片段了,要完整的包含layui.css、admin.css、layui.js,而且还要参照main.js配置:
layui.config({ base: getProjectUrl() + 'assets/module/' }).extend({ /// ......省略 }).use(['layer', 'admin'], function () { var $ = layui.jquery; var layer = layui.layer; var admin = layui.admin; });你可以把这段js写一个common.js作为公共引用,你可以直接让后端返回这样的menu数据格式,也可以在注册路由的时候格式化数据, index模块有介绍,getProjectUrl()这个方法可以让不同路径的子界面都可以正确配置模块位置,这个方法的实现在前面main.js章节中有介绍。
Layui没有像Bootstrap那样的直接写在页面中的模态弹窗,而是通过layer模块调用js弹出,对于用习惯了bootstrap了的可能不太适应, 下面介绍几种实现表单弹窗不同方式。
页面层弹窗就是弹窗页面和列表页面在一个html中,弹窗类型type=1:
<button id="btnAddUser" class="layui-btn">添加</button> <table id="tableUser" lay-filter="tableUser"></table> <!-- 表单弹窗,注意这里要用script包起来 --> <script type="text/html" id="modelUser"> <form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form"> <input name="userId" type="hidden"/> <div class="layui-form-item"> <label class="layui-form-label">用户名</label> <div class="layui-input-block"> <input name="nickName" class="layui-input" placeholder="请输入角色名" lay-verType="tips" lay-verify="required" required/> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">性别</label> <div class="layui-input-block"> <input type="radio" name="sex" value="男" title="男" checked/> <input type="radio" name="sex" value="女" title="女"/> </div> </div> <div class="layui-form-item text-right"> <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button> <button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button> </div> </form> </script> <!-- 表格操作列 --> <script type="text/html" id="tableBarUser"> <a class="layui-btn" lay-event="edit">修改</a> <a class="layui-btn" lay-event="del">删除</a> </script> <!-- js部分 --> <script> layui.use(['layer', 'form', 'table', 'admin'], function () { var $ = layui.jquery, layer = layui.layer, form = layui.form, table = layui.table, admin = layui.admin; // 渲染表格 var insTb = table.render({ elem: '#tableUser', url: '../../json/user.json', cols: [[ {field: 'nickName', title: '用户名'}, {field: 'sex', title: '性别'}, {toolbar: '#tableBarUser', title: '操作'} ]] }); // 添加 $('#btnAddUser').click(function () { showEditModel(); }); // 工具条点击事件 table.on('tool(tableUser)', function (obj) { var data = obj.data; var layEvent = obj.event; if (layEvent === 'edit') { // 修改 showEditModel(data); } else if (layEvent === 'del') { // 删除 layer.msg('点击了删除', {icon: 2}); } }); // 显示表单弹窗 function showEditModel(mUser) { admin.open({ type: 1, title: (mUser ? '修改' : '添加') + '用户', content: $('#modelUser').html(), // 注意这里有.html() success: function (layero, dIndex) { var url = mUser ? '/updateUser' : '/addUser'; form.val('modelUserForm', mUser); // 回显数据 // 表单提交事件 form.on('submit(modelSubmitUser)', function (data) { layer.load(2); $.post(url, data.field, function (res) { layer.closeAll('loading'); if (res.code == 200) { layer.close(dIndex); layer.msg(res.msg, {icon: 1}); insTb.reload(); // 保存成功刷新表格 } else { layer.msg(res.msg, {icon: 2}); } }, 'json'); return false; }); } }); } }); </script>通过content: $("#modelUser").html()是指定弹窗内容,表单使用<script type="text/html">而不是使用div, 这样表单就不会默认显示出来,操作表单的代码都要写在success里面,因为弹窗是把表单的html复制一份动态插入到页面上, 这种方式修改回显数据、修改完刷新表格都比较方便。
切记: 这种写法的弹窗所有操作弹窗内元素的代码都要放在弹窗的success里面。
iframe弹窗是弹窗内容页面和表格页面是两个页面,减少每个页面的代码量,弹窗类型type=2。
表格页面,list.html:
<button id="btnAddUser" class="layui-btn">添加</button> <table id="tableUser" lay-filter="tableUser"></table> <!-- 表格操作列 --> <script type="text/html" id="tableBarUser"> <a class="layui-btn" lay-event="edit">修改</a> <a class="layui-btn" lay-event="del">删除</a> </script> <!-- js部分 --> <script> layui.use(['layer', 'table', 'admin'], function () { var $ = layui.jquery, layer = layui.layer, table = layui.table, admin = layui.admin; // 渲染表格 var insTb = table.render({ elem: '#tableUser', url: '../../json/user.json', cols: [[ {field: 'nickName', title: '用户名'}, {field: 'sex', title: '性别'}, {toolbar: '#tableBarUser', title: '操作'} ]] }); // 添加 $('#btnAddUser').click(function () { showEditModel(); }); // 工具条点击事件 table.on('tool(tableUser)', function (obj) { var data = obj.data; var layEvent = obj.event; if (layEvent === 'edit') { // 修改 showEditModel(data); } else if (layEvent === 'del') { // 删除 layer.msg('点击了删除', {icon: 2}); } }); // 显示表单弹窗 function showEditModel(mUser) { var layIndex = admin.open({ type: 2, title: (mUser ? '修改' : '添加') + '用户', content: 'userForm.html', data: { user: mUser }, // 使用data参数传值给弹窗页面 end: function () { // 监听弹窗关闭 if (admin.getLayerData(layIndex, 'formOk')) { // 判断表单操作成功标识 insTb.reload(); // 成功刷新表格 } } }); } }); </script>弹窗页面,userForm.html:
<html> <head> <link rel="stylesheet" href="assets/libs/layui/css/layui.css"/> <link rel="stylesheet" href="assets/module/admin.css"/> </head> <body> <form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form"> <input name="userId" type="hidden"/> <div class="layui-form-item"> <label class="layui-form-label">用户名</label> <div class="layui-input-block"> <input name="nickName" class="layui-input" placeholder="请输入角色名" lay-verType="tips" lay-verify="required" required/> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">性别</label> <div class="layui-input-block"> <input type="radio" name="sex" value="男" title="男" checked/> <input type="radio" name="sex" value="女" title="女"/> </div> </div> <div class="layui-form-item text-right"> <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button> <button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button> </div> </form> <!-- js部分 --> <script type="text/javascript" src="assets/libs/layui/layui.js"></script> <script type="text/javascript" src="assets/js/common.js"></script> <script> layui.use(['layer', 'form', 'admin', 'formX'], function () { var $ = layui.jquery, layer = layui.layer, form = layui.form, admin = layui.admin, formX = layui.formX; var mUser = admin.getLayerData().user; // 获取列表页面传递的数据 // 回显数据,这里一定要用formX.val,form.val不能回显跨iframe的数据 formX.val('modelUserForm', mUser); // 表单提交事件 form.on('submit(modelSubmitUser)', function (data) { layer.load(2); var url = mUser ? '/updateUser' : '/addUser'; $.post(url, data.field, function (res) { layer.closeAll('loading'); if (res.code == 200) { layer.msg(res.msg, {icon: 1}); admin.putLayerData('formOk', true); // 设置操作成功的标识 admin.closeThisDialog(); // 关闭当前iframe弹窗 } else { layer.msg(res.msg, {icon: 2}); } }, 'json'); return false; }); }); </script> </body> </html>content参数写表单的页面url,在end回调里面判断操作成功的标识然后刷新表格,end是弹窗关闭的回调, 参数传递的详细介绍请看下面章节的详细介绍。
iframe弹窗的优点是页面独立,减少代码量,降低耦合,缺点是如果页面有下拉框、日期选择等,它们的范围不能超出弹窗的范围, 会导致弹窗出现滚动条,甚至不显示出来,下面章节介绍的url方式弹窗解决了这些缺点。
url方式弹窗就是使用url参数将弹窗页面独立出来,url参数admin.open特有的。
弹窗页面,userForm.html:
<!-- 注意这里不需要写`<html><body>`这些东西,它是一个html片段,不是完整的html页面 --> <form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form"> <input name="userId" type="hidden"/> <div class="layui-form-item"> <label class="layui-form-label">用户名</label> <div class="layui-input-block"> <input name="nickName" class="layui-input" placeholder="请输入用户名" lay-verType="tips" lay-verify="required" required/> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">性别</label> <div class="layui-input-block"> <input type="radio" name="sex" value="男" title="男" checked/> <input type="radio" name="sex" value="女" title="女"/> </div> </div> <div class="layui-form-item text-right"> <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button> <button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button> </div> </form> <script> layui.use(['layer', 'form', 'admin'], function () { var $ = layui.jquery, layer = layui.layer, form = layui.form, admin = layui.admin; var mUser = admin.getLayerData('#modelUserForm').user; // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可 form.val('modelUserForm', mUser); // 回显数据 // 表单提交事件 form.on('submit(modelSubmitUser)', function (data) { layer.load(2); var url = mUser ? '/updateUser' : '/addUser'; $.post(url, data.field, function (res) { layer.closeAll('loading'); if (res.code == 200) { layer.msg(res.msg, {icon: 1}); admin.putLayerData('formOk', true, '#modelUserForm'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可 admin.closeDialog('#modelUserForm'); // 关闭页面层弹窗 } else { layer.msg(res.msg, {icon: 2}); } }, 'json'); return false; }); }); </script>表格页面,list.html:
<button id="btnAddUser" class="layui-btn">添加</button> <table id="tableUser" lay-filter="tableUser"></table> <!-- 表格操作列 --> <script type="text/html" id="tableBarUser"> <a class="layui-btn" lay-event="edit">修改</a> <a class="layui-btn" lay-event="del">删除</a> </script> <!-- js部分 --> <script> layui.use(['layer', table', 'admin'], function () { var $ = layui.jquery, layer = layui.layer, table = layui.table, admin = layui.admin; // 渲染表格 var insTb = table.render({ elem: '#tableUser', url: '../../json/user.json', cols: [[ {field: 'nickName', title: '用户名'}, {field: 'sex', title: '性别'}, {toolbar: '#tableBarUser', title: '操作'} ]] }); // 添加 $('#btnAddUser').click(function () { showEditModel(); }); // 工具条点击事件 table.on('tool(tableUser)', function (obj) { var data = obj.data; var layEvent = obj.event; if (layEvent === 'edit') { // 修改 showEditModel(data); } else if (layEvent === 'del') { // 删除 layer.msg('点击了删除', {icon: 2}); } }); // 显示表单弹窗 function showEditModel(mUser) { var layIndex = admin.open({ title: (mUser ? '修改' : '添加') + '用户', url: 'userForm.html', data: { user: mUser }, // 传递数据到表单页面 end: function () { if (admin.getLayerData(layIndex, 'formOk')) { // 判断表单操作成功标识 insTb.reload(); // 成功刷新表格 } }, success: function (layero, dIndex) { // 弹窗超出范围不出现滚动条 $(layero).children('.layui-layer-content').css('overflow', 'visible'); } }); } }); </script>使用url参数指定弹窗的页面地址,url这个参数是admin.open新增的,这样做就会把表单页面的html使用ajax加载到弹窗中, 而不是iframe嵌入,这样表单里面的下拉框、日期等组件就可以超出弹窗的范围,不会导致弹窗出现滚动条。
注意:表单页面是html片段,表单页面和表格页面不要出现重复的id,因为最终是在一个页面上
第一种页面层弹窗由于所有关于弹窗内元素的操作都要写在弹窗的success里面, 部分人可能不适应这种方式,所以介绍第四种捕获层弹窗:
<!-- 表单弹窗,加display: none默认隐藏 --> <form style="display: none;" id="modelRoleForm" lay-filter="modelRoleForm" class="layui-form model-form"> <input name="roleId" type="hidden"/> <div class="layui-form-item"> <label class="layui-form-label">角色名</label> <div class="layui-input-block"> <input name="roleName" class="layui-input" placeholder="请输入角色名" lay-verType="tips" lay-verify="required" required/> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">备注</label> <div class="layui-input-block"> <textarea name="comments" placeholder="请输入内容" class="layui-textarea"></textarea> </div> </div> <div class="layui-form-item text-right"> <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button> <button class="layui-btn" lay-filter="modelSubmitRole" lay-submit>保存</button> </div> </form> <!-- js部分 --> <script> layui.use(['layer', 'form', 'table', 'admin'], function () { var $ = layui.jquery, layer = layui.layer, form = layui.form, table = layui.table,admin = layui.admin; var formUrl; // 渲染表格 var insTb = table.render({...}); // 添加 $('#btnAddRole').click(function () { showEditModel(); }); // 表格工具条点击事件 table.on('tool(tableRole)', function (obj) { var data = obj.data; var layEvent = obj.event; if (layEvent === 'edit') { // 修改 showEditModel(data); } }); // 显示编辑弹窗 function showEditModel(mRole) { $('#modelRoleForm')[0].reset(); // 重置表单 form.val('modelRoleForm', mRole); // 回显数据 formUrl = mRole ? 'role/update' : 'role/add'; admin.open({ type: 1, fixed: true, // 加这个可解决没有居中的问题 title: (mRole ? '修改' : '添加') + '角色', content: $('#modelRoleForm') // 这里是重点,没有.html() }); } // 表单提交事件 form.on('submit(modelSubmitRole)', function (data) { layer.load(2); $.post(formUrl, data.field, function (res) { layer.closeAll('loading'); if (res.code == 200) { admin.closeDialog('#modelRoleForm'); layer.msg(res.msg, {icon: 1}); insTb.reload(); } else { layer.msg(res.msg, {icon: 2}); } }, 'json'); return false; }); }); </script>与第一种的区别是form不用<script>包裹,加style="display:none"隐藏, admin.open的content是$('#roleForm')而不是$('#roleForm').html(), 表单的提交事件可以直接写在外面,而不用写在弹窗的success里面。
捕获层的弊端就是弹窗的页面代码最好是写在body下面,不然样式会被其他样式影响。
如果四种弹窗方式的用法都掌握了更好,这四种足以灵活应对各种业务场景了。
此方法是把layer弹窗自带的确定按钮绑定成表单的提交按钮:
<!-- 表单弹窗 --> <script type="text/html" id="modelUser"> <div class="layui-form-item"> <label class="layui-form-label">用户名</label> <div class="layui-input-block"> <input name="nickName" class="layui-input" lay-verType="tips" lay-verify="required" required/> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">备注</label> <div class="layui-input-block"> <textarea name="comments" class="layui-textarea"></textarea> </div> </div> </script> <!-- js部分 --> <script> layui.use(['layer', 'form', 'admin'], function () { var $ = layui.jquery, layer = layui.layer, form = layui.form, admin = layui.admin; admin.open({ type: 1, title: '添加用户', btn: ['确定', '取消'], content: $('#modelUser').html(), success: function (layero, dIndex) { // 把确定按钮绑定表单提交,参数二是给按钮起一个lay-filter,参数三是给表单起一个lay-filter admin.modelForm(layero, 'demoFormSubmit', 'demoForm'); // 给表单赋值 form.val('demoForm', {nickName: '张三', sex: '男'}); // 监听表单提交 form.on('submit(demoFormSubmit)', function (data) { layer.msg(JSON.stringify(data.field)); return false; }); }, yes: function () { // 确定按钮方法什么都不要操作 } }); }); </script>admin.modelForm()这个方法会把弹窗外面包一个form,然后把确定按钮加lay-submit,所以你的表单页面不需要写form和确定按钮,只需要写表单项。
这个方法的使用场景为你想要表单的按钮固定,只滚动表单内容部分,可以用这个操作, 当然前面介绍的4中方式也是支持固定按钮的,请到css组件样式中查看详细介绍。
参数传递:
admin.open({ type: 2, content: 'userForm.html', data: { name: '李白', sex: '男' } });通过data属性进行参数传递,data同样是admin.open新增的参数。
获取参数:
方法说明参数admin.getLayerData(index)获取某弹窗的全部参数layer的indexadmin.getLayerData(index, key)参数某弹窗参数的某个字段index,字段admin.getLayerData()iframe弹窗子页面获取参数无任何参数admin.getLayerData('#xxForm')url方式弹窗子页面获取参数弹窗内任意元素id如果是在iframe弹窗的子页面中可以使用admin.getLayerData()直接获取父页面传递的全部参数, 如果是在url方式打开的弹窗中可以使用admin.getLayerData('#xx')直接获取父页面的全部参数,'#xx'是弹窗内任意元素的id。
增加参数:
方法说明参数admin.putLayerData(key, value, index)增加参数字段名,值,indexadmin.putLayerData(key, value)iframe弹窗子页面增加参数字段名,值admin.putLayerData(key, value, '#xx')url方式弹窗子页面增加参数弹窗内任意元素id关于子页面向父页面传递参数,子页面put新参数,父页面根据弹窗的index取参数就可以了,上面四种方式弹窗中就有示例, 子页面修改成功了put了一个formOk的标识,父页面通过这个标识判断是否要刷新表格。
注意: data参数必须是对象的形式,data: 1、data: 'aa'这种写法会导致无法put新参数。
iframe弹窗可以用location.reload()或者iframe.contentWindow.location.reload()来刷新弹窗页面。
dialog-url.html代码:
<div>NAME: {{d.name}}</div> <div>SEX: {{d.sex}}</div> <a class="layui-btn" ew-event="closeDialog">关闭我</a> <script> layui.use(['layer'], function () { var $ = layui.jquery; }); </script>模板引擎语法同laytpl,d表示弹窗传递的数据,页面层也支持tpl: true,捕获层和iframe层不支持, 可以使用动态模板功能。
单页面是可以直接跨页面操作的:
table.reload('userTab', {}); // 刷新其他页面表格layui.use里面定义的变量想在其他页面访问:
layui.use(['layer'], function() { window.xxx = 'xxxx'; // 变量给其他页面访问 // 方法给其他页面访问 window.xxx = function(){ alert('aa'); }; });
部署只需要把前端所有代码放在nginx的html中,修改setter.js的baseServer为接口地址即可, 如果接口没有做跨域支持,可使用nginx反向代理来解决跨域问题:
打开“nginx/conf/nginx.conf”配置文件设置反向代理: http { server { # 加入以下配置,之前的配置全部不要动,这个location是新加入的 location /api/ { proxy_pass http://11.11.111.111:8088/; # 这个是后台接口所在的地址 } } } 修改setter.js里面的baseServer为/api/。这样配置接口就会访问localhost:80/api/,然后nginx再反向代理到实际接口。
在header中有几个选项:“xx系统”、“xx系统”,点击不同的系统切换不同的侧边菜单,查看演示。
侧边栏代码,使用nav-id标识不同的菜单:
<div class="layui-side"> <div class="layui-side-scroll"> <!-- 系统一的菜单,每个ul都加nav-id --> <ul nav-id="xt1" class="layui-nav layui-nav-tree" lay-filter="admin-side-nav"> <!-- ...省略代码... --> </ul> <!-- 系统二的菜单,加layui-hide隐藏 --> <ul nav-id="xt2" class="layui-nav layui-nav-tree layui-hide" lay-filter="admin-side-nav"> <!-- ...省略代码... --> </ul> </div> </div>header.html代码,使用nav-bind绑定侧边栏菜单:
<div class="layui-header"> <ul class="layui-nav layui-layout-left"> <li class="layui-nav-item"><a nav-bind="xt1">系统一</a></li> <li class="layui-nav-item"<a nav-bind="xt2">系统二</a></li> </ul> </div>
首先要去掉lay-shrink="_all"这个属性关闭手风琴,然后加layui-nav-itemed默认展开:
<ul class="layui-nav layui-nav-tree" lay-filter="admin-side-nav" lay-shrink="false"> <li class="layui-nav-item layui-nav-itemed"> ......省略 </li> <li class="layui-nav-item layui-nav-itemed"> ......省略 </li> </ul>3.1.8之前的版本由于修改了layui还需要在layui-nav-child上加style:
<ul class="layui-nav layui-nav-tree" lay-filter="admin-side-nav" lay-shrink="false"> <li class="layui-nav-item layui-nav-itemed"> <a>Dashboard</a> <dl class="layui-nav-child" style="display: block;"> <dd><a>工作台</a></dd> </dl> </li> </ul>
侧边栏折叠后图标会进行放大,如果要修改大小添加css修改font-size,如果不想放大改成14px:
@media screen and (min-width: 750px) { .layui-layout-admin.admin-nav-mini .layui-side .layui-nav .layui-nav-item > a > .layui-icon { font-size: 18px; } }
非iframe类型的弹窗才能解决(页面层、捕获层、url方式等):
admin.open({ type: 1, title: '添加用户', content: $('#model').html(), success: function (layero, index) { // 禁止出现滚动条,关键代码就是这句 $(layero).children('.layui-layer-content').css('overflow', 'visible'); } });
这个是针对手机屏幕下做了让弹窗宽度自适应,如果需要让弹窗宽度超出屏幕:
admin.open({ title: '添加用户', content: $('#model').html(), success: function (layero, index) { $(layero).css('max-width', 'unset'); // 去掉max-width属性,unset在ie无效可以用auto } });
layui的表单左边标题最多显示5个字,超出会换行,添加css修改宽度:
#userForm .layui-form-label { width: 100px; /* 这里修改标题宽度 */ } #userForm .layui-input-block { margin-left: 130px; /* 这里要比上面始终大30px */ }#userForm是表单的id,加id避免影响其他表单样式:
<form id="userForm" class="layui-form"> <div class="layui-form-item"> <label class="layui-form-label">活动起止时间</label> <div class="layui-input-block"> <input type="text" class="layui-input"/> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">活动详细介绍</label> <div class="layui-input-block"> <textarea class="layui-textarea" maxlength="200"></textarea> </div> </div> </form>
select(下拉框)、radio(单选框)等表单元素在layui中会被美化,对于动态生成的元素需要重新渲染才能美化:
$('div').appen('<select><option value="1">xxxx</option></select>'); form.render('select'); // 重新渲染select form.render('radio'); // 重新渲染radio form.render('checkbox'); // 重新渲染checkbox // 对于弹窗内select不显示 admin.open({ type: 1, content: '<select><option value="1">xxxx</option></select>', success: function(){ form.render('select'); // 弹窗要在success里重新渲染 } });另外需要注意父元素有layui-form这个class才会被layui识别并美化。
如果是因为窗口太小不能显示,可以加个trigger: 'click'解决:
laydate.render({ elem: '#xxx', type: 'date', trigger: 'click' });如果是弹窗中的laydate不显示,检查是否是在弹窗的success里面渲染的:
admin.open({ type: 1, content: $('#modelUser').html(), success: function (layero, dIndex) { laydate.render({ elem: '#xxx', type: 'date', }); } });
弹窗打开后焦点还位于点击打开的按钮上,就会导致按enter键无限打开,解决办法就是让焦点移到其他元素上, 下面是将焦点移到弹窗内表单提交按钮上:
admin.open({ type: 1, title: '添加用户', content: $('#userEditDialog').html(), success: function (layero, dIndex) { $(layero).find('[lay-submit]').focus(); } });
首先检查监听表单提交事件里面有没有写return false,表单里面如果有其他按钮要加type="button",如果还会导致点击按钮刷新页面, 一般是你的代码有报错的地方,建议将<form class="layui-form"></form>改成<div class="layui-form"></div>,form改成div对表单提交不会有任何影响, 只是不能按回车键提交表单,不能重置表单了,改成div后报错就不会导致刷新页面了,你可以看控制台里面的错误信息。
<form class="layui-form"> <div class="layui-form-item"> <div class="layui-inline"> <label class="layui-form-label">账号:</label> <div class="layui-input-inline"> <input name="username" class="layui-input" placeholder="输入账号"/> </div> </div> <div class="layui-inline"> <label class="layui-form-label">用户名:</label> <div class="layui-input-inline"> <input name="nickName" class="layui-input" placeholder="输入用户名"/> </div> </div> <div class="layui-inline"> <button class="layui-btn" lay-filter="userTbSearch" lay-submit>搜索</button> <button type="reset" class="layui-btn">重置</button> </div> </div> </form> <script> /* 监听表单提交 */ form.on('submit(userTbSearch)', function (data) { return false; }); </script>
在渲染和重载表格的时候加一个随机参数:
/* 渲染表格 */ var insTb = table.render({ elem: '#userTable', url: '../../json/user.json', where: {v: new Date().getTime()}, cols: [[ {type: 'checkbox'}, {type: 'numbers'}, {field: 'username', title: '账号', sort: true}, {field: 'nickName', title: '用户名', sort: true} ]] }); /* 重载表格 */ form.on('submit(userTbSearch)', function (data) { data.field.v = new Date().getTime(); insTb.reload({where: data.field, page: {curr: 1}}); return false; }); insTb.reload({where: {v: new Date().getTime()}, page: {curr: 1}});
如果用的是parent.layui.admin.open打开的弹窗,弹窗success里面的一些操作也应该相应的加parent:
function showEditModel(mData) { parent.layui.admin.open({ type: 1, title: (mData ? '修改' : '添加') + '用户', content: $('#userEditDialog').html(), success: function (layero, dIndex) { // 回显表单数据 parent.layui.formX.val('userEditForm', mData); // 表单提交事件 parent.layui.form.on('submit(userEditSubmit)', function (data) { return false; }); // 禁止弹窗出现滚动条 parent.layui.jquery(layero).children('.layui-layer-content').css('overflow', 'visible'); } }); }需要注意的是parent.layui.form.val不支持跨iframe的数据,可以使用formX模块,$应该用parent.layui.jquery。
如果你喜欢跨iframe操作,你需要了解window、parent、top:
<iframe id="child" src="child.html"></iframe> <script> layui.use(['jquery'], function() { var $ = layui.jquery; window.aaa = 'aaa'; // 定义变量到window下 // 定义方法到window下 window.bbb = function(str) { alert(str); }; // 访问iframe的变量 var childWin = $('#child')[0].contentWindow; console.log(childWin.ccc); childWin.location.reload(); // 刷新iframe页面 }); </script>child.html:
<script> layui.use(['jquery'], function() { window.ccc = 'ccc'; // 定义变量到window下 parent.aaa = '__a'; // 修改parent的变量 parent.bbb('bbb'); // 调用parent的方法 }); </script>
注意要两个div,样式加在里面的div上面。
这个问题一般是由于弹窗是url方式的弹窗,弹窗页面内又引入js、css导致的:
admin.open({ url: 'password.html' });url方式的弹窗页面是片段,不是完整的html,请不要包含<html>、<head>、<body>这些,不要重复引用layui.js:
<form class="layui-form model-form"> </form> <script> layui.use(['layer', 'form'], function () { }); </script>如果是主题、修改密码、便签、消息等弹窗出现的,是老版本使用的iframe弹窗方式,新版本默认改成url方式导致的,可升级对应的弹窗页面解决, 也可以配置成iframe弹窗:
<a ew-event="theme" data-type="2" data-content="page/tpl/tpl-theme.html">主题</a>
数据表格即使设置了page: false发送的请求还是会有page、limit这两个参数,page参数是无法去掉的,只能去掉limit参数:
table.render({ elem: '#userTable', url: '../../json/user.json', page: false, limit: null });后端可以判断如果limit参数为null就不分页查询全部,你也可以规定limit为0或者-1就查询全部。
用户管理演示了表格内开关switch的使用,这里只用到了数据的id修改状态, 如果要获取所在行的更多数据,可以通过加data-属性:
<script type="text/html" id="userTbState"> <input type="checkbox" lay-filter="userTbStateCk" value="{{d.userId}}" lay-skin="switch" data-name="{{d.username}}" data-sex="{{d.sex}}" lay-text="正常|锁定" {{d.state==0?'checked':''}}/> </script> <script> form.on('switch(userTbStateCk)', function (obj) { var id = obj.elem.value; var data = $(obj.elem).data(); console.log(id + data.name + data.sex); }); </script>也可以利用LAY_TABLE_INDEX获取:
<script type="text/html" id="userTbState"> <input type="checkbox" lay-filter="userTbStateCk" value="{{d.LAY_TABLE_INDEX}}" lay-skin="switch" lay-text="正常|锁定" {{d.state==0?'checked':''}}/> </script> <script> form.on('switch(userTbStateCk)', function (obj) { var index = obj.elem.value; var data = table.cache['userTable'][index]; console.log(data.userId + data.username); }); </script>表格中的下拉框select的用法与表格中的开关switch是一样的,可以参考使用:
<!-- 表格性别选择列 --> <script type="text/html" id="tbBasicTbSex"> <div class="ew-select-fixed"> <select lay-filter="tbBasicTbSexSel" data-index="{{d.LAY_INDEX}}"> <option value="">请选择</option> <option value="男" {{d.sex=='男'?'selected':''}}>男</option> <option value="女" {{d.sex=='女'?'selected':''}}>女</option> </select> </div> </script> <script> form.on('select(tbBasicTbSexSel)', function(obj){ var index = $(obj.elem).data('index'); var data = table.cache['userTable'][index]; console.log(data); // 得到当前行数据 console.log(obj.value); // 得到被选中的值 }); </script>
工具列中可以使用模板引擎语法:
<script type="text/html" id="userTbBar"> <a class="layui-btn" lay-event="view">查看</a> {{# if(d.state==0){ }} <a class="layui-btn" lay-event="send">发送</a> {{# }else if(d.state==1){ }} <a class="layui-btn" lay-event="back">撤回</a> {{# } }} <a class="layui-btn" lay-event="del">删除</a> </script>
layui的表单提交、赋值等不支持数组及嵌套的格式,可以通过简单的格式处理数据为自己想要的格式:
<form class="layui-form" lay-filter="demoForm"> <input name="name" class="layui-input"/> <input name="role__0" class="layui-input"/> <input name="role__1" class="layui-input"/> <input name="role__2" class="layui-input"/> <input name="sex__0" class="layui-input"/> <input name="sex__1" class="layui-input"/> <button class="layui-btn" lay-filter="demoSubmit" lay-submit>提交</button> </form> <script> /* 监听表单提交 */ form.on('submit(demoSubmit)', function (data) { var roleList = [], sexList = []; for(var f in data.field) { if(f.indexOf('role__') === 0) { roleList.push(data.field[f]); delete data.field[f]; } if(f.indexOf('sex__') === 0) { sexList.push(data.field[f]); delete data.field[f]; } } data.field.roleList = roleList; data.field.sexList = sexList; console.log(data.field); return false; }); /* 赋值 */ var res = {roleList:[], sexList: []}; for(var i=0;i<res.roleList.length;i++) { res['role__'+i] = res.roleList[i]; } form.val('demoForm', res); </script>
field写任意一个字段名称即可。
以最小日期限制举例:
/* 渲染日期 */ var insDate = laydate.render({ elem: '#test1', min: 7 }); var minConfig = insDate.config.min; // 获取配置的最小日期限制 /* 取消最小日期限制 */ insDate.config.min = {}; /* 恢复最小日期限制 */ insDate.config.min = minConfig;
以演示页面的权限管理为例,让类型为按钮不可选:
// 这个是treeTable提供的遍历数据的方法 insTb.eachData(function(i,item){ if (!item.isMenu) item.disabled = true; // 如果是按钮设置不可选 }); // 渲染下拉树 var insXmSel = xmSelect.render({ el: '#authoritiesEditParentSel', data: insTb.options.data, // 用treeTabled的数据 model: {label: {type: 'text'}}, prop: { name: 'authorityName', value: 'authorityId' }, radio: true, clickClose: true, tree: { show: true, indent: 15, strict: false, expandedKeys: true } });
模板页面/列表页/数据表格 中展示了表格中下拉框的使用,如何将下拉框的数据变为动态的(来源后端的):
var sexList = [{name: '男', value:0}, {name:'女', value: 1}]; var sexHtml = '<div><div class="ew-select-fixed"><select lay-filter="tbBasicTbSexSel">'; layui.each(sexList, function(i,item){ sexHtml += ('<option value="' + item.value + '">' + item.name + '</option>'); }); sexHtml += '</select></div></div>'; /* 渲染表格 */ var insTb = table.render({ elem: '#tbBasicTable', url: '../../../json/user.json', cols: [[ {type: 'checkbox'}, {type: 'numbers'}, {field: 'username', title: '账号', align: 'center', sort: true}, {field: 'sex', title: '性别', templet: sexHtml, sort: true} ]] });如果数据是ajax请求的,应该用同步的请求,或者在请求结束后再渲染表格:
var insTb, sexList = []; $.get('sex.json', function(res) { sexList = res.data; var sexHtml = '......省略'; /* 渲染表格,......省略具体代码 */ insTb = table.render({}); });
有一个刷新按钮,用的layui的刷新图标,点击后实现图标转圈,刷新完后停止转圈:
<i id="btnRefresh" class="layui-icon layui-icon-refresh"></i> <script> $('#btnRefresh').click(function() { $('#btnRefresh').removeClass('layui-anim layui-anim-rotate'); setTimeout(function() { $('#btnRefresh').addClass('layui-anim layui-anim-rotate'); // $('#btnRefresh').addClass('layui-anim layui-anim-rotate layui-anim-loop'); }); }); </script>多加一个classlayui-anim-loop是实现一直转圈,没有这个只会转一圈。
演示页面里面的下拉框之所以都是直接写的静态的,是因为实际项目中也可能后端渲染,也可能ajax渲染, 如果是前后端不分离项目建议直接用后端模板引擎渲染,如果是前后端分离方式需要用ajax渲染。
可以页面一进入就先获取下拉框的数据:
<form class="layui-form" lay-filter="userTbSearchForm"> 性别: <select name="sex"></select> </form> <!-- 表单弹窗 --> <script type="text/html" id="userEditDialog"> <form id="userEditForm" lay-filter="userEditForm" class="layui-form model-form"> 性别: <select name="sex" lay-verType="tips" lay-verify="required" required></select> </form> </script> <!-- js部分 --> <script> layui.use(['layer', 'form', 'table', 'formX'], function () { var $ = layui.jquery; var layer = layui.layer; var form = layui.form; var admin = layui.admin; var formX = layui.formX; var mSexList = []; // 所有性别 /* 获取性别数据字典 */ var loadIndex = layer.load(2); admin.req('sys/dictdata', {dictCode: 'sex'}, function (res) { layer.close(loadIndex); if (0 === res.code) { mSexList = res.data; // 渲染搜索栏中的性别select formX.renderSelect({ elem: '[lay-filter="userTbSearchForm"] select[name="sex"]', data: mSexList, name: 'dictDataName', value: 'dictDataId', hint: '请选择性别' }); } else { layer.msg(res.msg, {icon: 2}); } }); /* 显示表单弹窗 */ function showEditModel(mData) { admin.open({ type: 1, title: (mData ? '修改' : '添加') + '用户', content: $('#userEditDialog').html(), success: function (layero, dIndex) { form.val('userEditForm', mData); // 回显表单数据 // 渲染性别下拉 formX.renderSelect({ elem: '[lay-filter="userEditForm"] select[name="sex"]', data: mSexList, name: 'dictDataName', value: 'dictDataId', hint: '请选择性别', initValue: mData ? mData.sex : undefined }); } }); } }); </script>
像上面这样角色是动态循环出来的,想监听点击每一行的每个角色的事件,并且获取到点击的行的数据和点击的角色的数据, 获取行的数据table模块直接提供,如果要获取点击的角色对应的数据,就可以参考上面用data-index来实现。
用title加全选框,用templet加复选框,用d.LAY_INDEX获取序号。
在index.loadHome方法之后加js插入内容:
index.loadHome('......省略'); // 插入居中文字 $('.layui-body-header').addClass('text-center'); $('.layui-body-header-title').addClass('pull-left').css({'line-height': 'initial', 'margin-top': '10px'}) .after('<span>天气预报</span>');最后实现的效果:
增加循环滚动的效果,类似滚动公告,加一个marquee就实现了:
<span style="display: inline-block;height: 40px;"><marquee>天气预报</marquee></span>
layRouter是封装的一个简单的路由模块,给index模块作为支撑的,一般不需你要去直接用它, layRouter模块相对独立,你也可以拿出来用在自己的网站中,使用示例:
<a href="#/user">go user</a> <a href="#/home">go home</a> <div id="m"></div> <script> layui.use(['layRouter'], function() { var layRouter = layui.layRouter; layRouter.reg('#/home', function(routerInfo) { document.getElementById('m').innerHTML = 'Hello World'; }).reg('#/user', function(routerInfo) { document.getElementById('m').innerHTML = 'This is user page'; }); layRouter.init({ index: '/home' /* 首页地址 */ }); }); </script>打开例子后,会默认在id为m的div中显示 Hello World。 然后点击go user的链接,会跳转到 http://xx.com/#/user ,并且在id为m的div中显示 his is user page。
调用reg方法,参数一是路由关键字,参数二是路由触发的回调方法:
layRouter.reg('#/home', function(routerInfo) { console.log(routerInfo); }); // 参数一支持数组写法,并且#号是可以有可无的 layRouter.reg(['#/home', '/user', 'order'], function(routerInfo) { console.log(routerInfo); }); // 在easyweb框架中要使用这种,具体说明请前往index模块查看 index.regRouter([{ name: '用户管理', url: '#/system/user' }]);layRouter.reg是基础的写法,下面是index模块封装的写法,使用index.regRouter注册后访问#/system/user,就会打开一个“用户管理”的标签页,使用上面的写法不会。
在路由触发的回调里面routerInfo包含什么:
例如你访问 `#/system/user/id=1/sex=男` 它解析的信息为 { path: ["system", "user"], search: {id: 1, sex: "男"}, href: "/system/user/id=1/sex=男", refresh: false } path 路径数组search 参数列表refresh 如果是刷新触发的值为true
参数传递的规则:
#/system/user // 无参数 #/system/user/id=1 // 参数id=1 #/system/user/id=1/name=aaa // 参数id=1,name=aaa这三种类型的url注册路由的时候只会注册#/system/user这个url,后面两个都是加了参数,而且后面的参数可以无限加。
获取路由的参数:
var search = layui.router().search; var search = layui.router('#/system/user/id=1/name=aa').search; console.log(search);上面是layui提供的方法,建议使用layRouter模块封装的方法:
var search = layRouter.routerInfo().search; var search = layRouter.routerInfo('#/system/user/id=1/name=aa').search;layRouter封装的方法具有容错性,不管有没有#号都可以正确解析。
关于后端不能识别这种参数的解答:
有人建议参数用正常的?id=1&name=aa的方式,认为上面这种后端识别不了。首先spa版本一般都是前后端分离的, http://localhost/#/user/id=1这个url不会进入后端,路由地址也并不代表页面的地址, 访问#/user实际请求的页面是components/user.html,而且是ajax请求的。其次这种解析规则是layui自带的方法解析的。 最后为了满足前后端不分离的情况,3.1.6版本开始已经做了适配,当你访问#/user/id=1/name=aa时, ajax请求html页面会发送components/user.html?id=1&name=aa的请求,会把路由的参数带上,便于后端接收。
跳转页面(#号可写可不写):
layRouter.go('#/user'); layRouter.go('/user'); layRouter.go('user');刷新页面:
layRouter.refresh('#/user'); // #号也是可写可不写刷新页面与跳转页面的区别:
layRouter.reg('#/home', function(routerInfo) { console.log(routerInfo); });如果当前hash地址已经是#/home了,再go到home,回调是不会触发的,refresh会触发, 并且如果是刷新触发的,回调里面的routerInfo会多一个refresh字段值为true
在easyweb中处理路由不存在是setter.js中的routerNotFound方法。
重写pop方法即可监听路由的切换:
layRouter.pop = function(routerInfo) { console.log(routerInfo); };
下面演示如何从零实现一个单页面网站,项目结构:
|- layui |- page | |- home.html | |- order | |- product.html | |- introduction.html |- expand | |- layRouter.js |- index.htmlindex.html:
<html> <body> <a href="#/home">首页</a> <a href="#/order/product">产品</a> <a href="#/order/introduction">订单</a> <div id="LAY_MAIN"></div> <script type="text/javascript" src="layui/layui.js"></script> <script> layui.config({ base: 'expand' }).use(['jquery', 'layRouter'], function() { var $ = layui.jquery; var layRouter = layui.layRouter; layRouter.reg(['#/home', '#/order/product', '#/order/introduction'], function(routerInfo) { var path = routerInfo.path.join('/'); $('#LAY_MAIN').load('page/' + path + '.html'); }); layRouter.init({ index: '/home' }); }); </script> </body> </html>home.html:
<style> #LAY_HOME h2 { color: red; } </style> <div id="LAY_HOME"> <h2>This is Home page.</h2> </div> <script> layui.use(['layer'],function() { var layer = layui.layer; layer.msg('Hello'); }); </script>product.html、order.html可自行发挥,然后运行index.html就可以实现一个简单的单页面应用了。