1.1.导入项目
- 下载项目后进行解压
- 使用IDEA、WebStorm、HBuilder等前端开发工具打开
- 打开index.html点击右上角浏览器图标运行:
注意: 必须以http://的形式访问,而不是file://的形式访问。
1.2.项目结构
|-assets
| |-images // 图片
| |-js
| |-main.js // 入口js
| |-libs // 第三方库,echarts(图表)、layui
| |-module // layui扩展模块,版本更新只用替换此目录
| |-img // 框架用到的图片
| |-admin.css // 框架核心样式
| |-admin.js // admin模块
| |-index.js // index模块
| |-******** // 其他扩展模块,不一一列举
|-components // html子页面
|-json // 模拟数据
|-index.html // 主页面
main.js为入口js,上手使用项目前最好先看看它的说明。
1.3.index.html结构说明
<html>
<head>
<link rel="stylesheet" href="assets/libs/layui/css/layui.css"/>
<link rel="stylesheet" href="assets/module/admin.css"/>
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
<!-- 头部 -->
<div class="layui-header">...</div>
<!-- 侧边栏 -->
<div class="layui-side">...</div>
<!-- 主体部分 -->
<div class="layui-body">...</div>
<!-- 底部 -->
<div class="layui-footer">...</div>
</div>
<script type="text/javascript" src="assets/libs/layui/layui.js"></script>
<script type="text/javascript" src="assets/js/main.js"></script>
</body>
</html>
index.html就是这样固定的结构,一般不需要修改,只用在components下写子页面即可。
1.4.添加一个菜单
- 打开json/menus.json,在合适的位置添加一个菜单:
{"name": "xx管理", "url": "#/xxx"}
- 在components下面新建一个xxx.html页面
- 运行index.html,查看效果
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以"#/"开头才会受路由控制,实现局部加载。
1.5.main.js说明
layui.config({
version: true, // 版本号配置为true可解决页面缓存问题
base: 'assets/module/' // 配置layui扩展模块目录
}).extend({ // 配置每个模块分别所在的目录
notice: 'notice/notice',
steps: 'steps/steps'
}).use(['index', 'admin'], function () {
var $ = layui.jquery;
var index = layui.index;
var admin = layui.admin;
// 加载侧边栏
admin.req('menus.json', function (res) {
index.regRouter(res); // 注册路由
index.renderSide(res); // 渲染侧边栏
// 加载主页
index.loadHome({
url: '#/console/console',
name: '<i class="layui-icon layui-icon-home"></i>'
});
});
});
- layui.config是告诉layui扩展模块所在目录
- layui.extend是配置每个模块具体位置,像admin.js、index.js这些没有子目录的不用配置
- version:true可解决模块js、html页面缓存的问题,也可以配置一个固定的版本号
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。
1.6.setter模块
setter模块主要是用于给index模块和admin模块提供配置,可根据自己需要进行更改。
参数:
配置名 | 默认 | 说明 |
---|---|---|
baseServer | 'json/' | 接口地址,admin.req方法会自动加 |
tableName | 'easyweb-spa' | 前端缓存用的存储表名 |
pageTabs | false | 是否开启多标签 |
cacheTab | true | 是否记忆Tab |
openTabCtxMenu | true | 是否开启Tab右键菜单 |
maxTabNum | 20 | 最多打开多少个tab |
viewPath | 'components' | 视图位置 |
viewSuffix | '.html' | 视图后缀 |
defaultTheme | 默认主题 | |
reqPutToPost | true | req请求put方法变成post,delete变get,并自动加_method |
apiNoCache | true | 配置为true后ajax请求json数据不会加版本号 |
navArrow | 侧边栏导航箭头,可选''(layui默认)、'arrow2'(箭头)、'arrow3'(加减号) | |
closeFooter | false | 是否关闭页脚 |
tplOpen | '{{' | 模板引擎边界符 |
tplClose | '}}' | 模板引擎边界符 |
tabAutoRefresh | false | 是否切换Tab自动刷新页面 |
defaultLoading | 1 | 默认的加载动画(只控制admin.showLoading的默认) |
方法:
配置名 | 默认 |
---|---|
getToken() | 获取token |
putToken(token) | 缓存token |
removeToken() | 清除token |
getUser() | 获取当前登录的用户信息 |
putUser(user) | 缓存当前登录的用户信息 |
getUserAuths() | 获取用户所有权限 |
getAjaxHeaders(requestUrl) | ajax请求的统一header |
ajaxSuccessBefore(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;
});
*/
1.7.ID命名规范
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 等 |
输入类的 | Edt | EditText的简写,input 、textarea 等 |
选择类的 | Sel | 下拉框、多选下拉框等 |
表格 | Table | id="userTable" 、id="userOrderTable" 等 |
表格附加组件 | Tb | id="userTbBar" 、id="userOrderTbBar" 等 |
弹窗 | Dialog | id="userEditDialog 、id="userInfoDialog" 等 |
表单 | Form | id="userEditForm 、id="userInfoForm" 等 |
表单提交按钮 | Submit | id="userEditSubmit" 、id="userInfoSubmit" 等 |
页面的命名
<!-- 命名格式:xxxApp -->
<div class="layui-fluid" id="userApp">
<!-- 省略页面内容...... -->
</div>
以上规则只是规范,非必须要求
1.8.子页面模板语法
在子页面首行增加<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}}
2.1.批量注册路由
index.regRouter([{
name: '用户管理',
url: '#/system/user' // 路由关键字,必须要有#号
}]);
// 嵌入iframe网页形式
index.regRouter([{
name: '百度一下',
url: '#/baidu',
iframe: 'https://baidu.com'
}]);
// 格式化数据,可以处理后端返回的menu.json格式
index.regRouter(res.data, function (d) {
d.name = d.menuName;
d.url = d.path;
d.iframe = d.component;
d.icon = d.menuIcon;
d.show = !d.hide;
d.subMenus = d.children;
return d;
});
参数是一个数组,当你访问#/system/user
时,就会打开一个“用户管理”标签页,“url”是路由关键字, 对应的页面地址是“components/system/user.html”,“components”和“.html”可以在setter模块中配置。
2.2.加载默认主页
index.loadHome({
url: '#/console/console1',
name: '<i class="layui-icon layui-icon-home"></i>',
loadSetting: true
});
- url 必填,主页路径,必须要有#号
- name 必填,Tab标题
- iframe 非必填,嵌入iframe网页形式
- loadSetting 非必填,是否恢复记忆的Tab
只有调用了loadHome方法,路由模块才会真正开始工作。
2.3.打开一个选项卡
index.openNewTab({
name: '用户管理',
url: '#/system/user'
});
// 也可以加参数
index.openNewTab({
name: '用户管理',
url: '#/system/user/id=1'
});
// 打开一个iframe标签
index.openNewTab({
name: '百度一下',
url: '#/baidu',
iframe: 'https://baidu.com'
});
- name 选项卡的标题
- url 路由关键字,必须要有#号
- iframe 可选参数,打开一个iframe标签
也可以使用<a href="#/system/user">XX</a>
,这种写法必须保证“#/system/user”已经注册了路由,而“openNewTab()”会自动注册。
注意:openNewTab方法只是注册临时路由,如果刷新页面,路由就会不存在了,
如果想刷新页面也能使用,需要提前在main.js中的index.loadHome()
方法之前注册路由, 或者在menus.json中配置,并且可以加show: false
不在侧边栏中显示。
2.4.关闭指定选项卡
index.closeTab('#/system/user');
index.closeTab('/system/user');
index.closeTab('system/user');
#/
可写可不写,关闭当前tab可以用admin.closeThisTabs()
或者ew-event="closeThisTabs"
。
2.5.跳到指定选项卡
index.go('#/system/user');
index.go('/system/user');
index.go('system/user');
前面#/
可写可不写。
2.6.修改Tab标题
index.setTabTitle('Hello'); // 修改当前Tab标题文字,也支持单标签模式
index.setTabTitle('Hello', tabId); // 修改指定Tab标题文字
index.setTabTitleHtml('<span>Hello</span>'); // 修改整个标题栏的html,此方法只在单标签模式有效
index.setTabTitle(); // 单标签模式隐藏标题栏
关闭多标签会自动生成一个标题栏,可用此方法修改标题栏,参数为undefined
为隐藏标题栏。
2.7.获取hash路径
index.getHashPath('#/system/user');
index.getHashPath('#/system/user/id=1/name=aa');
这两种hash最后返回的都是system/user
,后面属于参数传递,不属于视图的路径。
2.8.侧边栏手风琴折叠
在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"。
2.9.切换Tab自动刷新
setter.js里面配置tabAutoRefresh: true
即可开启。
2.10.渲染侧边栏
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还可以格式化数据
3.1.全部方法
方法 | 参数 | 描述 |
---|---|---|
flexible(expand) | true和false | 折叠/展开侧导航 |
activeNav(url) | a标签的href无#号 | 设置侧导航栏选中 |
refresh(url) | url,可为空 | 刷新指定Tab或当前Tab |
closeAllTabs() | 无 | 关闭所有选项卡 |
closeOtherTabs(url) | url | 关闭除url外所有选项卡 |
closeThisTabs(url) | url,可为空 | 关闭url或当前选项卡 |
rollPage(d) | left、right、auto | 滚动选项卡tab |
changeTheme(theme,win,noCache,noChild) | 切换主题 | |
lockScreen(url) | 见单独 | 锁屏 |
unlockScreen(isRemove) | 见单独 | 解除锁屏 |
iframeAuto() | 无 | 让当前的iframe弹层自适应高度 |
closeThisDialog() | 无 | 关闭当前iframe弹窗 |
closeDialog(elem) | dom选择器 | 关闭elem元素所在的页面层弹窗 |
open(option) | 同layer | 打开弹窗 |
popupRight(option) | 同layer | 打开右侧弹窗 |
getLayerData(index,key) | 见弹窗专题 | 获取弹窗传递数据 |
putLayerData(key,value,index) | 见弹窗专题 | 弹窗传递数据 |
reloadLayer(index,url,success) | 见弹窗专题 | 刷新url方式弹窗 |
alert(content,options,yes) | 同layer | 封装layer.alert |
confirm(content,options,yes,cancel) | 同layer | 封装layer.confirm |
prompt(options,yes) | 同layer | 封装layer.prompt |
tips(options) | 见admin模块高级 | 增强layer.tips |
modelForm(layero,btnFilter,formFilter) | 见弹窗专题 | 把弹窗自带按钮跟表单绑定一起 |
getLayerIndex('#elem') | jq选择器 | 获取页面层弹窗index |
req(url,data,success,method,option) | 见单独 | 封装ajax |
ajax(option) | 同$.ajax | 封装ajax |
showLoading(elem,type,opacity,size) | 见单独 | 显示加载动画 |
removeLoading(elem,fade,del) | 见单独 | 移除加载动画 |
btnLoading(elem,text,loading) | 见单独 | 设置按钮为加载状态 |
putTempData(key,value,local) | 见单独 | 缓存数据 |
getTempData(key,local) | 见单独 | 获取缓存数据 |
putSetting(key,value) | 修改配置信息 | |
recoverState() | 恢复配置信息 | |
getPageHeight() | 无 | 获取浏览器高度 |
getPageWidth() | 无 | 获取浏览器宽度 |
chooseLocation(option) | 见admin模块高级 | 地图选择位置 |
cropImg(option) | 见admin模块高级 | 裁剪图片 |
strToWin(str) | 字符的parent.parent转对象 | |
openSideAutoExpand() | 无 | 开启鼠标移入侧边栏自动展开 |
openCellAutoExpand() | 无 | 开启鼠标移入单元格超出自动展开 |
util.Convert_BD09_To_GCJ02(point) | 见admin模块高级 | 百度地图坐标转高德地图坐标 |
util.Convert_GCJ02_To_BD09(point) | 见admin模块高级 | 高德地图坐标转百度地图坐标 |
util.animateNum(elem,isThd,delay,grain) | 见admin模块高级 | 动态数字 |
util.deepClone(obj) | 见admin模块高级 | 深度克隆对象 |
util.fullTextIsEmpty(text) | 见admin模块高级 | 判断富文本是否为空 |
util.removeStyle(elem,options) | 见admin模块高级 | 移除元素的style |
util.scrollTop(elem) | 见admin模块高级 | 滚动到顶部 |
util.tpl(html,data,openCode,closeCode) | 见admin模块高级 | 模板解析 |
hasPerm(auth) | 权限 | 判断当前登录的用户是否有权限 |
renderPerm() | 无 | 移除没有权限的dom元素 |
使用示例:
layui.use(['admin'], function () {
var admin = layui.admin;
var pageHeight = admin.getPageHeight(); // 获取浏览器高度
});
3.2.弹窗相关方法
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模块还封装了弹窗参数传递等更多方法,请到弹窗专题查看。
3.3.加载层loading
按钮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()
移除加载动画。
3.4.ajax封装
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等错误)。
3.5.缓存操作putTempData
admin.putTempData('key', 'value'); // sessionStorage存储
admin.putTempData('key'); // 不写value是删除
admin.putTempData('key', 'value', true); // localStorage存储
admin.putTempData('key', true); // 不写value是删除
admin.getTempData('key'); // 获取sessionStorage存储的key
admin.getTempData('key', true); // 获取localStorage存储key
admin.getTempData(); // 获取sessionStorage存储的全部数据
admin.getTempData(true); // 获取localStorage存储的全部数据
3.6.锁屏功能
admin.lockScreen(url); // 锁屏,url是锁屏页面地址,不填为默认地址
admin.unlockScreen(); // 解除锁屏(隐藏锁屏页面)
admin.unlockScreen(true); // 移除锁屏页面,下次锁屏需要重新加载页面
如果是自定义锁屏页面,页面不要包含head、body这些东西,只是代码片段,参考默认的锁屏页面。
3.7.ew-event事件绑定
使用示例:
<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>
3.8.open弹窗事件
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('打开了弹窗')"
。
3.9.logout事件
<a ew-event="logout"
data-url="login.html"
data-ajax="api/logout"
data-method="post"
data-code="200"
data-parse-data="return {code: res.status, msg: res.message}"
data-confirm="false">退出登录</a>
- data-url 必填 退出登录后跳转的页面地址
- data-ajax 非必填 退出登录需要请求的ajax接口
- data-method 非必填 ajax请求方式,默认是delete
- data-code 非必填 ajax返回成功的状态码,默认是0
- data-parse-data 非必填 处理接口返回的数据
- data-confirm 非必填 是否显示询问框,false表示直接退出不询问,默认true
3.10.判断权限hasPerm
通过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();
}
});
4.1.文字提示
鼠标滑过弹出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
});
4.2.地图选择位置
admin.chooseLocation({
needCity: true,
onSelect: function (res) {
layer.msg(JSON.stringify(res), {icon: 1});
}
});
参数 | 默认 | 描述 |
---|---|---|
title | "选择位置" | 弹窗标题 |
needCity | false | 是否返回行政区,省市区默认不返回 |
center | 定位当前城市 | 地图默认的中心点 |
defaultZoom | 11 | 地图默认缩放级别 |
pointZoom | 17 | 选中时地图的缩放级别 |
keywords | 无 | poi检索关键字,例如:建筑、写字楼 |
pageSize | 30 | poi检索最大数量 |
onSelect | 无 | 选择回调 |
mapJsUrl | 内置 | 高德地图js的url |
- 地图默认中心点参考值:[116.397428, 39.90923],经度,纬度
- 地图url参考值:
https://webapi.amap.com/maps?v=1.4.14&key=xxxxxxx
- 返回结果说明:
- res.name; // 地点名称
- res.address; // 详细地址
- res.lat; // 纬度
- res.lng; // 经度
- res.city; // 城市,是一个对象
- res.city.province; // 省
- res.city.city; // 市
- res.city.district; // 区
- res.city.citycode; // 城市代码
地图相关参数的详细介绍请前往高德地图API查看。
4.3.裁剪图片
admin.cropImg({
aspectRatio: 1/1,
imgSrc: '../../assets/images/15367146917869444.jpg',
onCrop: function (res) {
// 返回的res是base64编码的裁剪后的图片
layer.msg('<img src="' + res + '" width="220px" height="220px"/>');
}
});
参数 | 默认 | 描述 |
---|---|---|
title | "裁剪图片" | 弹窗标题 |
aspectRatio | 1/1 | 裁剪比例,例如:16/9 |
imgSrc | 无 | 要裁剪的图片,无则先弹出选择图片 |
imgType | 'image/jpeg' | 裁剪的图片类型,非必填 |
onCrop | 无 | 裁剪完成回调 |
limitSize | 不限制 | 限制选择的图片大小 |
acceptMime | 'image/*' | 限制选择的图片类型 |
exts | 不限制 | 限制选择的图片后缀 |
- acceptMime参考值:'image/jpg, image/png'(只显示 jpg 和 png 文件)
- exts参考值:jpg|png|gif|bmp|jpeg
后面三个参数配置可参考upload模块, 如果想单独在页面中使用图片裁剪功能请参考Cropper插件文档
4.2.动画数字
<h2 id="demoAnimNum1">12345</h2>
<h2 id="demoAnimNum2">¥2373467.342353</h2>
<h2 id="demoAnimNum3">上浮99.98%</h2>
<script>
layui.use(['admin'], function () {
var $ = layui.jquery;
var admin = layui.admin;
admin.util.animateNum('#demoAnimNum1');
admin.util.animateNum('#demoAnimNum2');
admin.util.animateNum('#demoAnimNum3', true, 500, 100);
});
</script>
- 参数一 需要动画的元素
- 参数二 是否开启千分位,开启后每进千加逗号分隔
- 参数三 动画间隔,默认500
- 参数四 动画粒度,默认100
此方法会智能过滤除数字外的字符,如果动态了修改了数字应该加$('#xx').data('num', '');
重置一下。
4.3.经纬度转换
快速使用:
// 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坐标系
不同的坐标系之间会有几十到几百米的偏移,所以在开发基于地图的产品时,可以通过此方法修正不同坐标系之间的偏差, 详细了解国内各坐标系。
4.4.深度克隆对象
var o1 = {name: 'xxx', role: ['admin', 'user']};
var o2 = admin.util.deepClone(o1);
对象型在参数传递时通常是引用传递,有时需要把对象克隆一份再传递,避免传递的参数被修改导致第二次传递出现问题。
4.5.判断富文本是否为空
admin.util.fullTextIsEmpty('<p><span>哈哈</span><img src="xxx.jpg"/>');
判断规则,有文字,有img、audio、video、iframe等标签都属于不为空。
4.6.移除指定style
<div style="color: red;background-color: green;">Hello</div>
像上面这样有多个内联样式style,如何只移除其中一个或多个呢:
admin.util.removeStyle('background-color');
admin.util.removeStyle(['background-color', 'color']);
4.7.模板引擎
var html = admin.util.tpl('<p>{{d.name}}</p>', {name: 'xxx'}, '{{', '}}');
- 参数一 模板内容
- 参数二 填充数据
- 参数三 非必填,边界符,默认
{{
- 参数四 非必填,边界符,默认
}}
它与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>
4.8.滚动到顶部
admin.util.scrollTop(); // 主体部分滚动到顶部
admin.util.scrollTop('html,body'); // 自定义元素滚动到顶部
admin.util.scrollTop('#xxDiv'); // 自定义元素滚动到顶部
由于spa版本多标签和单标签的滚动条所在元素都不一样,而且也不在body上, 所以封装了scrollTop方法可自动判断主体部分滚动条所在的元素。
4.9.事件监听
监听侧边栏折叠/展开:
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);
4.10.动态模板
直接赋值数据:
<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 远程数据的url
- method 远程数据的请求方式
- where 远程数据的请求条件,也可以
data-where="JSON.stringify({})"
- headers 远程数据的请求header
- content-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'"
这里先是双引号再是单引号,不要漏掉了。
5.1.公共类
类名(class) | 说明 |
---|---|
pull-left | 左浮动 |
pull-right | 右浮动 |
text-left | 内容居左 |
text-center | 内容居中 |
text-right | 内容居右 |
inline-block | 设置display为inline-block |
bg-white | 设置背景为白色 |
layui-link | 设置a标签颜色为主题色 |
layui-text | .layui-text下面的a标签为蓝色 |
text-muted | 文字颜色为灰色 |
text-success | 文字颜色为绿色,成功色 |
text-warning | 文字颜色为黄色警告色 |
text-danger | 文字颜色为红色危险色 |
text-info | 文字颜色为蓝色信息色 |
text-primary | 文字颜色为主题色 |
以上是easyweb增加的公共类,当然也可以使用Layui公共类。
5.2.组件样式
类名(class) | 说明 |
---|---|
icon-btn | 带图标的按钮,会缩小边距 |
icon-date | 在元素的右边加入日期的图标 |
icon-search | 在元素的右边加入搜索的图标 |
btn-circle | 圆形按钮,参见便签界面 |
arrow2 | 设置侧边栏小三角为箭头图标,加在layui-nav上 |
arrow3 | 设置侧边栏小三角为加减号图标,加在layui-nav上 |
close-footer | 关闭页脚,加在body上 |
hide-body-title | 全局隐藏单标签模式标题栏,加在body上 |
table-tool-mini | 数据表格工具栏mini样式,加在table父元素上 |
full-table | 针对full-xxx的table的工具栏mini样式 |
mini-bar | 如果有滚动条,使用细的风格 |
layui-form-select-top | 控制下拉框上弹出,加载select父元素上 |
ew-tb-cell-ck | 数据表格里面checkbox |
ew-field-group | easyweb的字段集辅助类 |
<!-- 图标按钮 -->
<button class="layui-btn icon-btn"><i class="layui-icon"></i>搜索</button>
<!-- 日期图标 -->
<input class="layui-input icon-date" type="text"/>
<!-- 搜索图标 -->
<input class="layui-input icon-search" type="text"/>
<!-- 圆形按钮 -->
<div class="btn-circle">
<i class="layui-icon layui-icon-add-1"></i>
</div>
<!-- 下拉框上弹出 -->
<div class="layui-form-select-top">
<select>....</select>
</div>
<!-- 关闭页脚 -->
<body class="layui-layout-body close-footer">
<!-- 表格工具栏mini样式 -->
<div class="table-tool-mini full-table">
<table id="xxTable" lay-filter="xxTable"></table>
</div>
<!-- 字段集嵌套数据表格 -->
<div class="ew-field-group">
<fieldset class="layui-elem-field">
<legend>我是标题</legend>
</fieldset>
<div class="ew-field-box">
<table id="demoFieldTb" lay-filter="demoFieldTb"></table>
</div>
</div>
5.3.表单弹窗
类名(class) | 说明 |
---|---|
model-form | 调整弹窗内的表单的间距使之更好看 |
model-form-body | 表单内容部分,高度自适应,超过屏幕高度显示滚动条 |
model-form-footer | 表单底部按钮部分,用于固定底部按钮 |
layui-form-required | 表单必填项加红色星号 |
表单弹窗示例:
<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
5.4.表格工具栏
类名(class) | 说明 |
---|---|
toolbar | 调整表格上面的表单间距使之更好看 |
w-auto | 设置width:auto,用于重置一些有固定宽度表单元素 |
mr0 | 设置margin-right:0,用于重置一些表单元素的样式 |
form-search-show-expand | 点击了展开才显示出来 |
form-search-expand | 表格搜索栏展开\折叠按钮默认样式 |
<!-- 表格顶部工具栏区域 -->
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label w-auto">账 号:</label>
<div class="layui-input-inline mr0">
<input name="username" class="layui-input" type="text" placeholder="输入账号"/>
</div>
</div>
<!-- 加了form-search-show-expand类在展开的时候才显示 -->
<div class="layui-inline form-search-show-expand">
<label class="layui-form-label w-auto">用户名:</label>
<div class="layui-input-inline mr0">
<input name="nickName" class="layui-input" type="text" placeholder="输入用户名"/>
</div>
</div>
<div class="layui-inline">
<button class="layui-btn icon-btn" lay-filter="formSubSearchUser" lay-submit>
<i class="layui-icon"></i>搜索
</button>
<!-- 添加这个按钮可展开/折叠表单 -->
<a class="layui-btn form-search-expand" search-expand>
展开 <i class="layui-icon layui-icon-down"></i>
</a>
</div>
</div>
</div>
<!-- 表格 -->
<table id="tableUser" lay-filter="tableUser"></table>
移动端自动适配效果:
表格搜索折叠/展开事件监听:
<!-- 直接写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>
5.5.垂直选项卡
只需要在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>
5.6.徽章扩展
<span class="layui-badge layui-badge-green">绿色</span>
<span class="layui-badge layui-badge-blue">蓝色</span>
<span class="layui-badge layui-badge-red">红色</span>
<span class="layui-badge layui-badge-yellow">黄色</span>
<span class="layui-badge layui-badge-gray">灰色</span>
用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>
5.7.下拉树单选
<div id="demoSel" class="ew-xmselect-tree"></div>
xmSelect下拉单选树增加一个ew-xmselect-tree
可以有更好的样式。
5.8.fixed方式select
只需要在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。
6.1.1.快速使用
<!-- click模式触发 -->
<div class="dropdown-menu">
<button class="layui-btn icon-btn"> Click me <i class="layui-icon layui-icon-drop"></i>
</button>
<ul class="dropdown-menu-nav">
<li><a>1st menu item</a></li>
<li><a>2nd menu item</a></li>
<li><a>3rd menu item</a></li>
</ul>
</div>
<!-- hover模式触发,增加dropdown-hover即可 -->
<div class="dropdown-menu dropdown-hover">
<button class="layui-btn icon-btn"> Hover me <i class="layui-icon layui-icon-drop"></i></button>
<ul class="dropdown-menu-nav">
<li><a>1st menu item</a></li>
<li><a>2nd menu item</a></li>
<li><a>3rd menu item</a></li>
</ul>
</div>
<script>
layui.use(['dropdown'], function () {
var dropdown = layui.dropdown; // 加载模块
});
</script>
6.1.2.更多样式
<!-- 标题及禁用样式 -->
<div class="dropdown-menu dropdown-hover">
<button class="layui-btn icon-btn"> 更多样式 <i class="layui-icon layui-icon-drop"></i></button>
<ul class="dropdown-menu-nav">
<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>
</div>
<!-- 带小三角样式 -->
<div class="dropdown-menu dropdown-hover">
<button class="layui-btn icon-btn"> 带小三角 <i class="layui-icon layui-icon-drop"></i>
</button>
<ul class="dropdown-menu-nav">
<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>
<!-- 暗色主题 -->
<div class="dropdown-menu">
<button class="layui-btn layui-btn-normal icon-btn"> 暗色主题 <i class="layui-icon layui-icon-drop"></i></button>
<ul class="dropdown-menu-nav dark"><!-- 加一个dark即可 -->
<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>
</div>
6.1.3.对任意元素使用
<div class="dropdown-menu dropdown-hover">
<input type="text" placeholder="一个会跳舞的输入框" class="layui-input"/>
<ul class="dropdown-menu-nav">
<li class="title">是不是在找</li>
<li><a>另一个会跳舞的下拉框</a></li>
<li><a>一杯忧郁的可乐</a></li>
</ul>
</div>
6.1.4.带遮罩层
遮罩层是分离式的绑定,通过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>
6.1.5.自定义下拉内容
<div class="dropdown-menu">
<button class="layui-btn layui-btn-normal icon-btn"> 克隆/下载 <i class="layui-icon layui-icon-drop"></i></button>
<div class="dropdown-menu-nav dropdown-bottom-right" style="width: 280px;padding: 0 10px 10px 10px;">
<div class="dropdown-anchor"></div>
<!-- 下面是自定义内容 -->
<div class="layui-tab layui-tab-brief">
<ul class="layui-tab-title">
<li class="layui-this">HTTPS</li>
<li>SSH</li>
</ul>
<div class="layui-tab-content" style="padding: 10px 0 10px 0;">
<div class="layui-tab-item layui-show">
<input class="layui-input" value="https://gitee.com/whvse/easyweb-jwt.git"/>
</div>
<div class="layui-tab-item">
<input class="layui-input" value="git@gitee.com:whvse/easyweb-jwt.git"/>
</div>
</div>
</div>
<button class="layui-btn layui-btn-sm layui-btn-fluid" style="margin-bottom: 10px;">Download ZIP</button>
<img src="http://p1.music.126.net/voV3yPduAhNATICMRJza1A==/109951164017919367.jpg" width="100%">
<!-- //end.自定义内容结束 -->
</div>
</div>
6.1.6.控制显示方向
在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>
6.1.7.在数据表格中使用
数据表格中使用分离式的绑定,并且加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>
6.1.8.气泡确认框
<!-- 下弹出 -->
<div class="dropdown-menu">
<button class="layui-btn layui-btn-primary icon-btn"> 气泡确认框 <i class="layui-icon layui-icon-drop"></i></button>
<div class="dropdown-menu-nav dropdown-popconfirm">
<div class="dropdown-anchor"></div>
<div class="dropdown-popconfirm-title">
<i class="layui-icon layui-icon-help"></i>这是一段内容确定删除吗?
</div>
<div class="dropdown-popconfirm-btn">
<button class="layui-btn" btn-cancel>取消</button>
<button class="layui-btn layui-btn-normal">确定</button>
</div>
</div>
</div>
<!-- 上弹出 -->
<div class="dropdown-menu">
<button class="layui-btn layui-btn-primary icon-btn"> 气泡确认框 <i class="layui-icon layui-icon-drop"></i></button>
<div class="dropdown-menu-nav dropdown-popconfirm dropdown-top-right">
<div class="dropdown-anchor"></div>
<div class="dropdown-popconfirm-title">
<i class="layui-icon layui-icon-about"></i>这是一段内容确定删除吗?
</div>
<div class="dropdown-popconfirm-btn">
<button class="layui-btn" btn-cancel>取消</button>
<button class="layui-btn layui-btn-normal">确定</button>
</div>
</div>
</div>
6.2.1.快速使用
layui.use(['notice'], function(){
var notice = layui.notice;
// 消息通知
notice.success({
title: '消息通知',
message: '你有新的消息,请注意查收!'
});
// 提示框,1成功、2失败、3警告、4加载、5信息(蓝色图标)
notice.msg('Hello', {icon: 1});
});
6.2.2.全部方法
方法 | 参数 | 说明 |
---|---|---|
success(object) | 见下方 | 成功消息(绿色) |
warning(object) | 见下方 | 警告消息(黄色) |
error(object) | 见下方 | 错误消息(红色) |
info(object) | 见下方 | 通知消息(蓝色) |
show(object) | 见下方 | 自定义样式 |
msg(string, object) | 文本,其他参数 | 提示框 |
destroy() | 无 | 关闭全部 |
hide(object, toast, closedBy) | 见下方 | 关闭指定的通知 |
settings(object) | 见下方 | 统一设置默认值 |
统一设置默认值:
notice.settings({
timeout: 1500,
transitionIn: 'flipInX',
onOpened: function(){
console.log('notice opend!');
}
});
关闭指定的通知:
notice.hide({}, document.querySelector('.toast'));
- 参数一 重写一些参数,比如关闭动画等
- 参数二 根据自定义的className选择关闭的对象
6.2.3.参数列表
参数 | 说明 | 默认值 | 可选值 |
---|---|---|---|
title | 标题 | 无 | string类型 |
message | 内容 | 无 | string类型 |
position | 显示位置 | topRight | 见下方 |
transitionIn | 进入动画 | fadeInLeft | 见下方 |
transitionOut | 退出动画 | fadeOutRight | 见下方 |
timeout | 消失时间 | 5000 | 单位毫秒,false永不消失 |
progressBar | 进度条 | true | true显示、false不显示 |
balloon | 气泡效果 | false | true开启、false关闭 |
close | 关闭按钮 | true | true显示,false不显示 |
pauseOnHover | 鼠标滑过暂停消失时间 | true | true、false |
resetOnHover | 鼠标滑过重置消失时间 | false | true、false |
animateInside | 文字动画效果 | false | true开启、false关闭 |
className | 自定义class | 无 | 多个用空格分隔 |
theme | 主题 | light | light、dark |
audio | 音效 | 无 | 1,2,3,4,5,6 |
image | 显示图片 | 无 | 图片地址 |
imageWidth | 图片宽度 | 60 | 数字 |
buttons | 显示按钮 | [] | [ [ 'btn1', function(){} ], ['btn2', function(){} ] ] |
overlay | 遮罩层 | false | true显示,false不显示 |
drag | 滑动关闭 | true | true开启,false关闭 |
layout | 布局类型 | 2 | 1标题和内容并排,2两排显示 |
rtl | 布局方向 | false | false内容居左,true居右 |
displayMode | 显示模式 | 0 | 0无限制,1同类型存在不显示,2同类型存在先移除 |
targetFirst | 插入方式 | 自动 | true从上插入,false下插入 |
onOpened | 打开后回调函数 | 无 | function |
onClosed | 关闭后回调函数 | 无 | function |
titleColor | 标题颜色 | 默认 | 颜色单位 |
titleSize | 标题大小 | 默认 | 尺寸单位 |
messageColor | 文字颜色 | 默认 | 颜色单位 |
messageSize | 文字大小 | 默认 | 尺寸单位 |
backgroundColor | 背景颜色 | 默认 | 颜色单位 |
progressBarColor | 进度条颜色 | 默认 | 颜色单位 |
maxWidth | 最大宽度 | 空 | 尺寸单位 |
参数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 | 翻转退出 |
6.2.4.解决弹窗遮挡
<!-- 页面加入样式 -->
<style>
body .iziToast-overlay {
z-index: 19892001;
}
body .iziToast-wrapper {
z-index: 19892002;
}
</style>
6.3.1.快速使用
<input id="demoCascader1" placeholder="请选择" class="layui-hide"/>
<script>
layui.use(['cascader'], function () {
var $ = layui.jquery;
var cascader = layui.cascader;
cascader.render({
elem: '#demoCascader1',
data: [{
value: 'beijing',
label: '北京',
children: [{
value: 'gugong',
label: '故宫'
},{
value: 'tiantan',
label: '天坛'
},{
value: 'wangfujing',
label: '王府井'
}]
}]
});
// 请求远程数据
cascader.render({
elem: '#demoCascader1',
reqData: function (values, callback, data) {
$.get('xxxx.json', function(res){
callback(res.data); // 数据请求完成通过callback回调
},'json');
}
});
// 处理数据格式,如果你的数据没有value、label字段可以前端处理
cascader.render({
elem: '#demoCascader1',
reqData: function (values, callback, data) {
$.get('xxxx.json', function(res){
function each(data) {
for (var i = 0; i < data.length; i++) {
data[i].value = data[i].id;
data[i].label = data[i].name;
if (data[i].children) {
each(data[i].children);
}
}
}
each(res.data);
callback(res.data);
},'json');
}
});
// 如果你的数据不是children形式,可以前端转换
cascader.render({
elem: '#demoCascader1',
reqData: function (values, callback, data) {
$.get('xxxx.json', function(res){
callback(pidToChildren(res.data, 'id', 'pid', 0));
},'json');
}
});
/* pid转children形式 */
function pidToChildren(data, idName, pidName, childName, pId) {
if (!childName) childName = 'children';
var newList = [];
for (var i = 0; i < data.length; i++) {
if (data[i][idName] == data[i][pidName]) return;
if (pId === undefined) pId = 0;
if (data[i][pidName] == pId) {
var children = pidToChildren(data, idName, pidName, childName, data[i][idName]);
if (children.length > 0) data[i][childName] = children;
newList.push(data[i]);
}
}
return newList;
}
});
</script>
6.3.2.异步加载
<input id="demoCascader1" placeholder="请选择" class="layui-hide"/>
<script>
layui.use(['cascader'], function () {
var $ = layui.jquery;
var cascader = layui.cascader;
cascader.render({
elem: '#demoCascader1',
reqData: function (values, callback, data) {
// values是当前所有选中的值,data是当前选中的对象
$.get('xxxx.json', { id: data.value }, function(res){
callback(res.data); // 数据请求完成通过callback回调
},'json');
}
});
});
</script>
异步加载的数据格式为:
[
{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');
}
});
6.3.3.自定义分隔符
<input id="demoCascader1" placeholder="请选择" class="layui-hide"/>
<script>
layui.use(['cascader'], function () {
var $ = layui.jquery;
var cascader = layui.cascader;
cascader.render({
elem: '#demoCascader1',
data: [],
renderFormat: function (labels, values) {
return labels.join(' / '); // 默认是用斜杠分割,可以自定义
}
});
});
</script>
6.3.4.搜索功能
<input id="demoCascader1" placeholder="请选择" class="layui-hide"/>
<script>
layui.use(['cascader'], function () {
var $ = layui.jquery;
var cascader = layui.cascader;
cascader.render({
elem: '#demoCascader1',
data: [],
filterable: true // 这个参数是开启搜索功能
});
// 自定义搜索
cascader.render({
elem: '#demoCascader1',
data: [],
filterable: true,
reqSearch: function (keyword, callback, dataList) {
// keyword是搜索的关键字,dataList是当前的全部数据集合
$.get('xxx.json', { keyword: keyword }, function(res){
callback(res);
},'json')
}
});
});
</script>
搜索后端接口返回的数据格式为:
[
{"value": "1,2", "label": "北京 / 王府井"},
{"value": "1,3", "label": "北京 / 故宫"}
]
如果要标记关键字为红色:<span class=\"search-keyword\">北京</span> / 王府井
6.3.5.省市区选择
<input id="demoCascader1" placeholder="请选择" class="layui-hide"/>
<script type="text/javascript" src="/assets/module/cascader/citys-data.js"></script>
<script>
layui.use(['cascader'], function () {
var $ = layui.jquery;
var cascader = layui.cascader;
cascader.render({
elem: '#demoCascader1',
data: citysData,
itemHeight: '250px',
filterable: true
});
});
</script>
省市区的数据已经封装好了,只需要引入数据的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)));
6.3.6.全部方法
<input id="demoCascader1" placeholder="请选择" class="layui-hide"/>
<script>
layui.use(['cascader'], function () {
var $ = layui.jquery;
var cascader = layui.cascader;
var ins1 = cascader.render({
elem: '#demoCascader1',
data: []
});
ins1.data; // 获取当前的数据
ins1.open(); // 展开
ins1.hide(); // 关闭
ins1.removeLoading(); // 移除加载中的状态
ins1.setDisabled(true); // 禁用或取消禁用
ins1.getValue(); // 获取选中的数据值
ins1.getLabel(); // 选取选中的数据名称
ins1.setValue('1,2'); // 设置值
});
</script>
6.3.7.全部参数
参数名称 | 介绍 | 默认值 |
---|---|---|
elem | 需要渲染的元素 | |
data | 数据 | |
clearable | 是否开启清除 | true |
clearAllActive | 清除时清除所有列选中 | false |
trigger | 次级菜单触发方式,可选'hover' | 'click' |
disabled | 是否禁用 | false |
changeOnSelect | 是否点击每一项都改变值 | false |
filterable | 是否开启搜索功能 | false |
notFoundText | 搜索为空是提示文字 | '没有匹配数据' |
itemHeight | 下拉列表的高度 | '180px' |
reqData(values, callback, data) | 异步获取数据的方法 | |
reqSearch(keyword, callback, dataList) | 自定义搜索的方法 | |
renderFormat(labels, values) | 选择后用于展示的函数 | |
onChange(values, data) | 数据选择改变的回调 | |
onVisibleChange(isShow) | 展开和关闭的回调 |
6.4.2.全部参数
参数 | 说明 | 默认值 |
---|---|---|
defaultText | 提示文字 | +请输入 |
skin | 样式风格 | |
removeWithBackspace | 回退键可删除已添加的标签 | false |
focusWithClick | 点击已添加标签输入框获取焦点 | true |
autocomplete_url | 自动提示接口url | |
autocomplete | 接口配置 |
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 二次修改。
6.5.1.快速使用
<div class="split-group">
<div class="split-item" id="demoSplit1">
面板一
</div>
<div class="split-item" id="demoSplit2">
面板二
</div>
</div>
<script>
layui.use(['Split'], function () {
var $ = layui.jquery;
var Split = layui.Split;
// 水平分割,需要分割的元素(id)、默认大小(百分比)、最小值(单位px)
Split(['#demoSplit1', '#demoSplit2'], {sizes: [25, 75], minSize: 100});
});
</script>
6.5.2.垂直分割
<div class="split-group-vertical">
<div class="split-item" id="demoSplit3">
面板一
</div>
<div class="split-item" id="demoSplit4">
面板二
</div>
</div>
<script>
layui.use(['Split'], function () {
var $ = layui.jquery;
var Split = layui.Split;
// 垂直分割
Split(['#demoSplit3', '#demoSplit4'], {direction: 'vertical'});
});
</script>
6.5.3.嵌套使用
<div class="split-group" style="height: 600px;">
<div class="split-item" id="demoSplit8">
<div class="split-group-vertical">
<div class="split-item" id="demoSplit10">
面板一
</div>
<div class="split-item" id="demoSplit11">
面板二
</div>
</div>
</div>
<div class="split-item" id="demoSplit9">
面板三
</div>
</div>
<script>
layui.use([Split'], function () {
var $ = layui.jquery;
var Split = layui.Split;
// 垂直水平分割
Split(['#demoSplit8', '#demoSplit9'], {sizes: [25, 75], minSize: 100});
Split(['#demoSplit10', '#demoSplit11'], {direction: 'vertical'});
});
</script>
6.6.1.快速使用
<div id="demoProgress1"></div>
<script>
layui.use(['CircleProgress'], function () {
var CircleProgress = layui.CircleProgress;
// 快速使用
new CircleProgress('#demoProgress1', {
max: 100,
value: 20
});
});
</script>
6.6.2.全部参数
参数名称 | 说明 | 默认值 |
---|---|---|
max | 最大值 | |
value | 当前值 | |
clockwise | 是否顺时针方向 | true |
startAngle | 起始角度 | 0 |
textFormat | 文字样式 |
文字样式:
- vertical 垂直
- percent 百分比
- value 只显示值
- valueOnCircle 值显示在进度条上
- none 不显示文字
自定义文字样式:
new CircleProgress('#demoProgress9', {
max: 12,
value: 9,
textFormat: function (value, max) {
return value + ' dots';
}
});
6.6.3.自定义样式
使用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
6.7.1.快速使用
默认样式:
<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的基础上增加的样式。
6.7.2.风格选择
迷你样式:
<!-- 多加一个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>
注意 简洁风格只有标题,没有图标、描述等
6.7.3.上一步下一步
<div class="layui-tab layui-steps" lay-filter="demoSteps">
<!-- 省略...... -->
</div>
<script>
layui.use(['steps'], function () {
var steps = layui.steps;
steps.next('demoSteps'); // 下一步
steps.prev('demoSteps'); // 上一步
steps.go('demoSteps', 2); // 指定跳转,索引从0开始
});
</script>
也可以使用属性绑定:
<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>
7.1.1.全部方法
方法 | 参数 | 描述 |
---|---|---|
merges(tableId, indexs, fields) | 见单独说明 | 合并单元格 |
bindCtxMenu(tableId, items) | 见单独说明 | 给表格行绑定鼠标右键 |
exportData(object) | 见单独说明 | 导出任意数据 |
exportDataX(object) | 见单独说明 | 导出任意数据(依赖社区excel模块) |
exportDataBack(object) | 见单独说明 | 后端导出任意数据(支持post提交参数) |
render(object) | 同layui表格 | 渲染表格,带后端排序功能 |
renderFront(object) | 同layui表格 | 渲染表格,前端分页、排序、搜索 |
layui.use(['tableX'], function () {
var tableX = layui.tableX;
});
7.1.2.合并单元格
在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
7.1.3.行绑定鼠标右键
在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);
}
}];
}
});
7.1.4.后端排序
tableX.render({
elem: '#xTable3',
url: '../../json/user.json',
cols: [[
{type: 'numbers'},
{field: 'nickName', title: '用户名', sort: true},
{field: 'sex', title: '性别', sort: true}
]]
});
仅仅是把table.render换成tableX.render
就会在点击排序时自动传递sort
和order
参数,例如:user?page=1&limit=10&sort=sex&order=asc。
- sort 排序字段,值是点击排序列的field
- order 排序方式,升序是asc,降序是desc
注意:如果写了sort: true开启排序,一定要写field: 'xxx'
7.1.5.前端分页排序
tableX.renderFront({
elem: '#xTable1',
url: '../../json/userAll.json',
page: true,
cols: [[
{field: 'nickName', title: '用户名', sort: true},
{field: 'sex', title: '性别', sort: true}
]]
});
仅仅是把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
表格展示时有效,排序和导出时屏蔽
7.1.6.导出数据
tableX.exportData({
cols: insTb.config.cols, // 表头配置
data: table.cache.xTable3, // 数据,支持url方式
fileName: '用户表' // 文件名称
});
// exportDataX是新增的方法,参数与上面一摸一样
tableX.exportDataX({});
参数 | 必填 | 说明 | 默认 |
---|---|---|---|
cols | 是 | 表头配置 | |
data | 是 | 导出的数据,支持数组和string的url | |
fileName | 否 | 导出的文件名称 | table |
expType | 否 | 导出的文件类型 | xls(默认)、csv、xlsx |
option | 否 | url方式的配置 |
如果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可解决此问题。
7.1.7.导出全部、搜索
后端分页时如何导出全部数据:
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把搜索条件传给后端。
7.1.8.后端导出
后端导出本质就是下载文件,一般用window.open(url)即可,如果要传递参数、post提交,那就麻烦了,tableX进行了完美的封装:
tableX.exportDataBack('user/export', {sex: '男'});
tableX.exportDataBack('user/export', {sex: '男'}, 'post');
- 参数一 后端的url
- 参数二 传递的参数
- 参数三 请求方式
如果是get方式,会使用?和&拼接参数,如果是post方式,会创建一个隐藏的表单来提交, 如果你的参数有复杂的类型,比如json格式,建议用post的形式,此方法也可以用来做post方式下载文件的操作。
7.2.1.验证规则
规则 | 描述 | 提示信息 |
---|---|---|
phoneX | 手机号 | 请输入正确的手机号 |
emailX | 邮箱 | 邮箱格式不正确 |
urlX | 网址 | 链接格式不正确 |
numberX | 数字 | 只能填写数字 |
dateX | 日期 | 日期格式不正确 |
identityX | 身份证 | 请输入正确的身份证号 |
psw | 密码 | 密码必须5到12位,且不能出现空格 |
equalTo | 重复 | 两次输入不一致 |
digits | 整数 | 只能输入整数 |
digitsP | 正整数 | 只能输入正整数 |
digitsN | 负整数 | 只能输入负整数 |
digitsPZ | 非负整数 | 只能输入正整数和0 |
digitsNZ | 非正整数 | 只能输入负整数和0 |
h5 | 兼容h5的规则 |
使用示例:
<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等的区别是如果没有输入不会验证,输入了才验证格式。
7.2.2.扩展方法
方法 | 描述 |
---|---|
val(filter, object) | 赋值表单,解决top.layui.form.val()无效bug |
renderSelect(option) | 渲染select封装 |
startTimer(elem, time, format) | 按钮验证码倒计时封装 |
formUpdatedField(field, oldField) | 获取表单修改过的数据 |
使用方法:
layui.use(['formX'],function(){
var formX = layui.formX;
// 赋值表单,支持top.formX.val()用法
formX.val('userForm', {name: 'user01'});
});
7.2.3.渲染select封装
<select id="sel"></select>
<script>
layui.use(['formX'],function(){
var formX = layui.formX;
// 数据方式
formX.renderSelect({
elem: '#sel',
data: [
{id: 1, name: '张三'},
{id: 2, name: '李四'}
],
name: 'name',
value: 'id',
hint: '请选择用户',
initValue: 1,
done: function() {}
});
// 异步方式
formX.renderSelect({
elem: '#sel',
data: 'user.json',
name: 'name',
value: 'id',
hint: '请选择用户',
done: function(data) {},
method: 'get',
where: {page: 20},
header: {token: 'xxx'},
async: true,
error: function(xhr, res){}
});
});
</script>
基础参数:
- elem 要渲染的select
- data 数据源,可以是数组,也可以是url
- name 显示的字段名称
- value 值的字段名称
- hint 未选择提示文字
- initValue 默认回显的数据
- done 渲染完成后的回调
- error data为url时请求失败的回调
- method data为url时请求方式
- where data为url时请求参数
- header data为url时请求header
- async data为url时请求时否是异步
后端返回数据格式为{"data": [], "code": 0}
,data有数据就是请求成功,没有数据就进入error,code等于多少都可以, 如果数据不是对象的形式,name和value可不填:
formX.renderSelect({
elem: '#sel',
data: ['张三','李四']
});
7.2.4.验证码倒计时
<button id="btnSend" class="layui-btn">发送验证码</button>
<script>
layui.use(['formX'],function(){
var formX = layui.formX;
// 按钮倒计时30s
formX.startTimer('#btnSend', 30);
// 自定义倒计时文字,默认是30s,29s,28s...
formX.startTimer('#btnSend', 30, function(time){
return time + 's 后可继续发送';
});
});
</script>
7.2.5.获取修改字段
var user = {sex: 'male', age: 18};
form.val('userForm', user); // 表单回显数据
// 获取表单修改后的字段
var field = formX.formUpdatedField(form.val('userForm'), user);
console.log(field);
利用这个方法可以做判断用户是否修改了表单:
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;
});
7.3.1.打印当前页面
printer.print();
printer.print({
hide: ['.layui-btn', '#btn01'], // 打印时隐藏的元素
horizontal: true, // 是否横向打印
blank: true, // 是否打开新页面打印
close: true, // 如果是打开新页面,打印完是否关闭
iePreview: true // 是否兼容ie打印预览
});
参数都是可以选参数,默认值已经符合大多数需求。
7.3.2.设置不打印元素
<div class="hide-print">非打印内容</div>
加hide-print
这个class即可,可以跟hide
参数叠加使用。
7.3.3.打印自定义内容
var pWindow = printer.printHtml({
html: '<span>xxxx</span>', // 要打印的内容
horizontal: true, // 是否横向打印
blank: true, // 是否打开新页面打印
close: true, // 如果是打开新页面,打印完是否关闭
iePreview: true, // 是否兼容ie打印预览
print: true // 如果是打开新窗口是否自动打印
});
打印表格时可以给table加print-table
这个class来设置表格的边框样式:<table class="print-table">
。
如果print为false关闭自动打印,可以使用
pWindow.print()
触发打印。
7.3.4.分页打印
printer.printPage({
htmls: [
'<span>xxxx</span>', // 要打印的内容
'<span>xxxx</span>'
],
style: '<style>span{color:red;}</style>',// 页面样式
horizontal: true, // 是否横向打印
blank: true, // 是否打开新页面打印
close: true, // 如果是打开新页面,打印完是否关闭
iePreview: true, // 是否兼容ie打印预览
print: true, // 如果是打开新窗口是否自动打印
padding: undefined, // 页面间距
debug: false, // 调试模式
height: undefined, // 页面高度
width: undefined // 页面宽度
});
参数都是可选参数:
- htmls 数组,代表每一页;
- style 样式,也可以写
<link rel="stylesheet">
的形式; - padding 页面间距,例如写'10px';
- debug 调试模式,每一页会加红色边框,便于调试大小;
7.3.5.拼接html
var htmlStr = printer.makeHtml({
title: '网页标题',
style: '<link rel="stylesheet" href="layui.css"><script type="text/javascript" src="layui.js"><\/script>',
body: $('#xxx').html()
});
7.4.1.快速使用
layui.use(['contextMenu'], function () {
var contextMenu = layui.contextMenu;
// 重写整个页面右键菜单
contextMenu.bind('html', [{
icon: 'layui-icon layui-icon-snowflake',
name: '菜单一',
click: function (e, event) {
// 通过$(event.currentTarget)可获取事件触发的元素
alert('点击了菜单一');
}
}, {
name: '菜单二',
click: function (e) {
alert('点击了菜单二');
}
}]);
});
- 参数一 绑定元素
- 参数二 菜单数组
菜单数组可支持无限极:
[{
icon: 'xxxxx',
name: '菜单三',
subs: [{
icon: 'xxxxx',
name: '子菜单一',
click: function (e, event) {
alert('点击了子菜单一');
}
}]
}]
- icon 图标
- name 菜单名
- click 点击事件
- subs 子菜单(支持无限极)
click事件里面的e是右键菜单的事件对象,event才是绑定的目标元素的事件对象
7.4.2.自定义使用
例如用于点击事件:
$('#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 原始事件对象
7.4.3.动态元素绑定
对于动态生成的元素可以使用事件委托的方式来绑定:
// 对.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;
});
7.5.1.快速使用
后台管理页面大多用数据表格展示,如果要使用网格、瀑布流、卡片列表等形式,分页、自动渲染、搜索、排序等功能都需要自己实现, 极其麻烦,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>
7.5.2.全部参数
参数 | 类型 | 说明 | 示例 |
---|---|---|---|
elem | String/DOM | 指定容器的选择器或DOM | "#demo" |
templet | String | item模板,模板遵循 laytpl 语法 | |
data | Array | 直接赋值数据,也可前端分页 | [{},{},{}] |
url | String | 接口地址 | |
method | String | 接口http请求类型,默认:get | |
where | Object | 接口的请求参数 | where: {id: 123} |
headers | Object | 接口的请求头 | headers: {token: 'sasasas'} |
contentType | String | 发送到服务端的内容编码类型 | contentType: 'application/json' |
parseData | Function | 数据格式解析的回调函数 | |
request | Function | 用于对分页请求的参数重新设定名称 | 默认是page、limit |
useAdmin | Boolean | 是否使用admin.ajax | 默认false |
done | Function | 数据渲染完的回调 | |
page | Object | 开启分页,配置分页参数 | |
loadMore | Object | 开启加载更多,配置加载更多参数 |
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; // 总数量
}
});
7.5.3.分页功能
使用page参数开启分页,page参数同layui分页组件的参数一致前往查看。
dataGrid.render({
elem: '#demoGrid', // 容器
templet: '#demoGridItem', // 模板
url: 'json/list.json', // 数据接口
page: {limit: 5} // 开启分页
});
7.5.4.加载更多功能
dataGrid.render({
elem: '#demoGrid', // 容器
templet: '#demoGridItem', // 模板
url: 'json/list.json', // 数据接口
loadMore: {limit: 5} // 开启加载更多
});
loadMore和page只能二选一,loadMore的可选参数:
参数 | 说明 | 默认值 |
---|---|---|
class | 自定义class | '' |
limit | 每页多少条 | 10 |
text | 显示文字 | '加载更多' |
loadingText | 加载中文字 | '加载中...' |
noMoreText | 无数据文字 | '没有更多数据了~' |
errorText | 加载失败文字 | '加载失败,请重试' |
7.5.5.实例方法
var ins = dataGrid.render({});
ins.reload(); // 重载
ins.reload({page: {curr: 2} }); // 跳到第二页
ins.update(index, fields, type); // 修改数据
// index是索引,fields是要修改的字段
// type 0整个item更新, 1只更新item子元素, 2只更新数据不更新item
ins.del(index); // 删除item
ins.getData(); // 获取当前数据
ins.checkStatus(); // 获取选中行数据
7.5.6.自动渲染
<div id="demoGrid" lay-data="{url: 'json/list.json', page: {limit: 5} }" data-grid>
<script type="text/html" data-grid-tpl>
<div>
<h2>{{d.title}}</h2>
<p>{{d.content}}</p>
<a lay-event="edit">修改</a>
</div>
</script>
</div>
通过添加data-grid和data-grid-tpl属性,dataGrid组件便会自动渲染,通过lay-data属性进行参数配置。
7.6.1.快速使用
layui.use(['fileChoose'], function () {
var fileChoose = layui.fileChoose;
fileChoose.open({
fileUrl: '', // 文件查看的url
listUrl: '../template/file/files.json', // 文件列表的url
where: {
access_token: 'xxxxxx'
},
num: 3, // 最多选择数量
dialog: {
offset: '60px'
},
onChoose: function (urls) {
layer.msg('你选择了:' + JSON.stringify(urls), {icon: 1});
}
});
});
7.6.2.全部参数
参数 | 描述 | 默认值 |
---|---|---|
fileUrl | 文件查看的url | |
listUrl | 文件列表的url | |
where | 文件列表请求参数 | {} |
num | 文件选择的数量 | 1 |
onChoose | 选择后回调 | |
upload | 文件上传配置(同layui配置) | {} |
dialog | 弹窗配置(同layui配置) | {} |
menu | 点击弹出的菜单 | 数组类型 |
menuClick | 菜单点击事件处理 | |
response | 接口数据格式化 |
菜单配置及点击事件:
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判断的。
8.1.鼠标滚轮监听
此插件来源于 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
可以得到浏览器实际的滚动距离。
8.2.二维码模块
此插件来源于 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>
8.3.引导插件
此插件来源于 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>
8.4.剪贴板
此插件来源于 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选择器都可以。
8.5.视频播放器
视频播放器使用的是西瓜视频开源的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>
8.6.富文本编辑器
富文本编辑器使用的是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>
9.1.主题功能
默认的样式以及内置的几套主题都在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,这种方法只适合固定一套主题不需要切换的情况。
9.2.自定义扩展模块
集成社区模块:
把下载好的模块放在/assets/module/
下面,如果模块是一个js,不用做任何配置就可以使用了, 例如admin.js、index.js,如果模块是一个文件夹,还需要在main.js
中配置模块的具体位置,如notice/notice
。
自定义扩展模块:
如果需要自己写一个扩展模块,请前往阅读Layui定义模块的开发文档, 或者参考admin.js、index.js等模块。
集成jquery插件:
jquery作为老牌框架,有着丰富的第三方插件,把插件放入libs下,页面中先引入jquery,再引入插件,即可使用。 当然更友好的集成方式是把它改造成layui扩展模块,改造方式请参考layui 封装第三方组件。
9.3.spa全部用iframe
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章节中有介绍。
10.弹窗专题
Layui没有像Bootstrap那样的直接写在页面中的模态弹窗,而是通过layer模块调用js弹出,对于用习惯了bootstrap了的可能不太适应, 下面介绍几种实现表单弹窗不同方式。
10.1.第一种 页面层弹窗
页面层弹窗就是弹窗页面和列表页面在一个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里面。
10.2.第二种 iframe弹窗
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方式弹窗解决了这些缺点。
10.3.第三种 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,因为最终是在一个页面上
10.7.第四种 捕获层弹窗
第一种页面层弹窗由于所有关于弹窗内元素的操作都要写在弹窗的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下面,不然样式会被其他样式影响。
10.4.四种方式选择指南
方式 | 推荐 | 理由 |
---|---|---|
第一、四种 | 建议初学者使用这种 | 不涉及两个页面传值问题 |
第二种iframe | 推荐表单跟表格无交互使用,比如详情 | 下拉框、日期不能超出弹窗 |
第三种url | 表单弹窗、页面有交互建议使用 | 页面传值方便,不存在问题 |
如果四种弹窗方式的用法都掌握了更好,这四种足以灵活应对各种业务场景了。
10.5.admin.modelForm方法
此方法是把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组件样式中查看详细介绍。
10.6.参数传递方法详解
参数传递:
admin.open({
type: 2,
content: 'userForm.html',
data: {
name: '李白',
sex: '男'
}
});
通过data属性进行参数传递,data同样是admin.open新增的参数。
获取参数:
方法 | 说明 | 参数 |
---|---|---|
admin.getLayerData(index) | 获取某弹窗的全部参数 | layer的index |
admin.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) | 增加参数 | 字段名,值,index |
admin.putLayerData(key, value) | iframe弹窗子页面增加参数 | 字段名,值 |
admin.putLayerData(key, value, '#xx') | url方式弹窗子页面增加参数 | 弹窗内任意元素id |
关于子页面向父页面传递参数,子页面put新参数,父页面根据弹窗的index取参数就可以了,上面四种方式弹窗中就有示例, 子页面修改成功了put了一个formOk的标识,父页面通过这个标识判断是否要刷新表格。
注意: data参数必须是对象的形式,data: 1、data: 'aa'这种写法会导致无法put新参数。
10.7.刷新url方式弹窗
admin.reloadLayer(index, url, success);
- index 弹窗的index,可以用
admin.getLayerIndex('#elem')
获取页面层index - url 用新的url刷新(可不填)
- success 刷新完成的回调,function类型(可不填)
iframe弹窗可以用location.reload()或者iframe.contentWindow.location.reload()来刷新弹窗页面。
10.8.弹窗使用模板引擎
admin.open({
url: 'dialog-url.html',
data: {
name: '妲己',
sex: '女'
},
tpl: true // 增加tpl:true开启模板引擎
});
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层不支持, 可以使用动态模板功能。
11.1.跨页面操作
单页面是可以直接跨页面操作的:
table.reload('userTab', {}); // 刷新其他页面表格
layui.use里面定义的变量想在其他页面访问:
layui.use(['layer'], function() {
window.xxx = 'xxxx'; // 变量给其他页面访问
// 方法给其他页面访问
window.xxx = function(){
alert('aa');
};
});
11.2.nginx部署解决跨域
部署只需要把前端所有代码放在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再反向代理到实际接口。
11.3.多系统模式
在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>
11.4.logo文字换行显示
<div class="layui-logo">
<img src="assets/images/logo.png"/>
<cite style="display: inline-block;line-height: inherit;margin-top: -4px;">
EasyWeb<br/>iframe
</cite>
</div>
11.5.侧边栏全部展开
首先要去掉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>
11.6.侧边栏折叠图标放大
侧边栏折叠后图标会进行放大,如果要修改大小添加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;
}
}
11.7.弹窗下拉框出现滚动条
非iframe类型的弹窗才能解决(页面层、捕获层、url方式等):
admin.open({
type: 1,
title: '添加用户',
content: $('#model').html(),
success: function (layero, index) {
// 禁止出现滚动条,关键代码就是这句
$(layero).children('.layui-layer-content').css('overflow', 'visible');
}
});
11.8.弹窗宽度不能超出屏幕
这个是针对手机屏幕下做了让弹窗宽度自适应,如果需要让弹窗宽度超出屏幕:
admin.open({
title: '添加用户',
content: $('#model').html(),
success: function (layero, index) {
$(layero).css('max-width', 'unset'); // 去掉max-width属性,unset在ie无效可以用auto
}
});
11.9.表单文字出现换行
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>
11.10.select、radio不显示
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识别并美化。
11.11.日期laydate不能显示
如果是因为窗口太小不能显示,可以加个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',
});
}
});
11.12.弹窗打开后按enter无限打开
弹窗打开后焦点还位于点击打开的按钮上,就会导致按enter键无限打开,解决办法就是让焦点移到其他元素上, 下面是将焦点移到弹窗内表单提交按钮上:
admin.open({
type: 1,
title: '添加用户',
content: $('#userEditDialog').html(),
success: function (layero, dIndex) {
$(layero).find('[lay-submit]').focus();
}
});
11.13.表单提交post变成了get
首先检查监听表单提交事件里面有没有写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>
11.14.修改表格背景和边框颜色
/* 表格背景颜色 */
.layui-table tbody tr:hover, .layui-table thead tr,
.layui-table-click, .layui-table-header, .layui-table-hover, .layui-table-mend,
.layui-table-patch, .layui-table-tool, .layui-table-total, .layui-table-total tr,
.layui-table[lay-even] tr:nth-child(even) {
background-color: #f5f7fa;
}
/* 表格边框颜色 */
.layui-table td, .layui-table th, .layui-table-col-set, .layui-table-fixed-r,
.layui-table-grid-down, .layui-table-header, .layui-table-page, .layui-table-tips-main,
.layui-table-tool, .layui-table-total, .layui-table-view, .layui-table[lay-skin=line], .layui-table[lay-skin=row] {
border-color: #ebeef5;
}
11.15.修改placeholder颜色
.layui-input::-webkit-input-placeholder, .layui-textarea::-webkit-input-placeholder {
color: #ccc;
}
.layui-input::-moz-placeholder, .layui-textarea::-webkit-input-placeholder {
color: #ccc;
}
.layui-input::-ms-input-placeholder, .layui-textarea::-webkit-input-placeholder {
color: #ccc;
}
body xm-select > .xm-tips {
color: #ccc;
}
11.16.IE数据表格缓存严重
在渲染和重载表格的时候加一个随机参数:
/* 渲染表格 */
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}});
11.17.使用parent.layer问题
如果用的是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>
11.18.表格打印太长列未换行
// 如果phone这一列全是字母和数字并且很长打印的时候不会自动换行
{field: 'phone', title: '手机号'}
// 改成这样就会自动换行了
{
field: 'phone', title: '手机号',
templet: '<div><div style="word-break: break-all;">{{d.phone}}</div></div>'
}
注意要两个div,样式加在里面的div上面。
11.19.弹窗layer.js报错404
这个问题一般是由于弹窗是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>
11.20.表格请求去掉page和limit
数据表格即使设置了page: false
发送的请求还是会有page
、limit
这两个参数,page参数是无法去掉的,只能去掉limit参数:
table.render({
elem: '#userTable',
url: '../../json/user.json',
page: false,
limit: null
});
后端可以判断如果limit参数为null就不分页查询全部,你也可以规定limit为0或者-1就查询全部。
12.1.表格内switch获取行数据
用户管理演示了表格内开关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>
12.2.表格回显复选框
var insTb = table.render({
elem: '#userTable',
url: '../../json/user.json',
cols: [[
{type: 'checkbox'},
{type: 'numbers'},
{field: 'username', title: '账号', sort: true},
{field: 'nickName', title: '用户名', sort: true}
]],
parseData: function(res) {
res.data[3].LAY_CHECKED = true; // 让第4条数据选中
return res;
}
});
12.3.表格工具列动态显示
工具列中可以使用模板引擎语法:
<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>
12.4.表单提交数组
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>
12.5.重载表格重置排序
insTb.reload({
where: {}, page: {curr: 1},
initSort: {field: 'createTime', type: null}
});
field写任意一个字段名称即可。
12.6.laydte动态控制日期限制
以最小日期限制举例:
/* 渲染日期 */
var insDate = laydate.render({
elem: '#test1',
min: 7
});
var minConfig = insDate.config.min; // 获取配置的最小日期限制
/* 取消最小日期限制 */
insDate.config.min = {};
/* 恢复最小日期限制 */
insDate.config.min = minConfig;
12.7.下拉树一些数据不可选
以演示页面的权限管理为例,让类型为按钮不可选:
// 这个是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
}
});
12.8.修改弹窗禁用输入框
/* 显示表单弹窗 */
function showEditModel(mData) {
admin.open({
type: 1,
title: (mData ? '修改' : '添加') + '用户',
content: $('#userEditDialog').html(),
success: function (layero, dIndex) {
if(mData) { // 表示是修改
// 把账号输入框变为只读
$(layero).find('[name="username"]').attr('readonly', 'readonly');
// 把密码输入框移除
$(layero).find('[name="password"]').parent().parent().remove();
}
}
});
}
12.9.表格中下拉框数据动态
模板页面/列表页/数据表格 中展示了表格中下拉框的使用,如何将下拉框的数据变为动态的(来源后端的):
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({});
});
12.10.实现点击后转一圈
有一个刷新按钮,用的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
是实现一直转圈,没有这个只会转一圈。
12.11.页面下拉框数据动态
演示页面里面的下拉框之所以都是直接写的静态的,是因为实际项目中也可能后端渲染,也可能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>
12.12.表格动态tool事件处理
<!-- 表格操作列 -->
<script type="text/html" id="userTbBar">
{{# layui.each(d.roles, function(i, item) { }}
<a class="layui-btn" lay-event="role" data-index="{{i}}">{{item.roleName}}</a>
{{# }); }}
</script>
<script>
table.render({
elem: '#userTable',
cols: [[
{field: 'username', title: '账号'},
{field: 'sex', title: '性别'},
{title: '角色', toolbar: '#userTbBar'}
]]
});
/* 表格工具条点击事件 */
table.on('tool(userTable)', function (obj) {
var d = obj.data;
if (obj.event === 'role') {
var i = $(this).data('index');
console.log(d.roles[i]);
}
});
</script>
像上面这样角色是动态循环出来的,想监听点击每一行的每个角色的事件,并且获取到点击的行的数据和点击的角色的数据, 获取行的数据table模块直接提供,如果要获取点击的角色对应的数据,就可以参考上面用data-index
来实现。
12.13.表格复选框和序号列合并
table.render({
elem: '#userTable',
cols: [[
{
title:'<input type="checkbox" name="layTableCheckbox" lay-skin="primary" lay-filter="layTableAllChoose">',
templet: '<div><input type="checkbox" name="layTableCheckbox" lay-skin="primary"> {{d.LAY_INDEX}}</div>'
},
{field: 'username', title: '账号'},
{field: 'sex', title: '性别'}
]]
});
用title
加全选框,用templet
加复选框,用d.LAY_INDEX
获取序号。
12.14.表单提交带文件上传
<form class="layui-form" id="demoForm">
<input type="file" name="file" class="layui-input"/>
<input type="text" name="title" class="layui-input"/>
<button class="layui-btn" lay-filter="demoSubmit" lay-sunmit>提交</button>
</form>
<script>
form.on('submit(demoSubmit)', function (data) {
var formData = new FormData();
for(var f in data.field) {
if(!data.field.hasOwnProperty(f)) continue;
formData.append(f, data.field[f]);
}
$('#demoForm [type="file"]').each(function() {
formData.append($(this).attr('name'), $(this)[0].files[0]);
});
$.ajax({
url:'/url',
type:'post',
processData:false,
contentType:false,
data:formData,
success:function (res) {
console.log(res);
}
});
return false;
});
</script>
12.15.单标签模式加居中文字
在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>
13.1.基础用法
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。
13.2.注册路由
调用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
13.3.路由参数传递
参数传递的规则:
#/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
的请求,会把路由的参数带上,便于后端接收。
13.4.跳转页面
跳转页面(#号可写可不写):
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
13.5.路由不存在处理
layRouter.init({
index: '/home',
notFound: function(routerInfo) {
}
});
在easyweb中处理路由不存在是setter.js中的routerNotFound
方法。
13.6.路由切换监听
重写pop
方法即可监听路由的切换:
layRouter.pop = function(routerInfo) {
console.log(routerInfo);
};
13.7.做一个单页网站
下面演示如何从零实现一个单页面网站,项目结构:
|- layui
|- page
| |- home.html
| |- order
| |- product.html
| |- introduction.html
|- expand
| |- layRouter.js
|- index.html
index.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就可以实现一个简单的单页面应用了。