EASY spa单页面版文档

1.1.导入项目

  1. 下载项目后进行解压
  2. 使用IDEA、WebStorm、HBuilder等前端开发工具打开
  3. 打开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.添加一个菜单

  1. 打开json/menus.json,在合适的位置添加一个菜单:
    {"name": "xx管理", "url": "#/xxx"}
  2. 在components下面新建一个xxx.html页面
  3. 运行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'前端缓存用的存储表名
pageTabsfalse是否开启多标签
cacheTabtrue是否记忆Tab
openTabCtxMenutrue是否开启Tab右键菜单
maxTabNum20最多打开多少个tab
viewPath'components'视图位置
viewSuffix'.html'视图后缀
defaultTheme 默认主题
reqPutToPosttruereq请求put方法变成post,delete变get,并自动加_method
apiNoCachetrue配置为true后ajax请求json数据不会加版本号
navArrow 侧边栏导航箭头,可选''(layui默认)、'arrow2'(箭头)、'arrow3'(加减号)
closeFooterfalse是否关闭页脚
tplOpen'{{'模板引擎边界符
tplClose'}}'模板引擎边界符
tabAutoRefreshfalse是否切换Tab自动刷新页面
defaultLoading1默认的加载动画(只控制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
输入类的EdtEditText的简写,inputtextarea
选择类的Sel下拉框、多选下拉框等
表格Tableid="userTable"id="userOrderTable"
表格附加组件Tbid="userTbBar"id="userOrderTbBar"
弹窗Dialogid="userEditDialogid="userInfoDialog"
表单Formid="userEditFormid="userInfoForm"
表单提交按钮Submitid="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', '&nbsp;保存...');   // 设置按钮为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"选择位置"弹窗标题
needCityfalse是否返回行政区,省市区默认不返回
center定位当前城市地图默认的中心点
defaultZoom11地图默认缩放级别
pointZoom17选中时地图的缩放级别
keywordspoi检索关键字,例如:建筑、写字楼
pageSize30poi检索最大数量
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"裁剪图片"弹窗标题
aspectRatio1/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">性&emsp;别:</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-groupeasyweb的字段集辅助类
<!-- 图标按钮 -->
<button class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</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">账&emsp;号:</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">&#xe615;</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">&nbsp;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">&nbsp;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">&nbsp;更多样式 <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">&nbsp;带小三角 <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">&nbsp;暗色主题 <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">&nbsp;克隆/下载 <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">&nbsp;气泡确认框 <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">&nbsp;气泡确认框 <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进度条truetrue显示、false不显示
balloon气泡效果falsetrue开启、false关闭
close关闭按钮truetrue显示,false不显示
pauseOnHover鼠标滑过暂停消失时间truetrue、false
resetOnHover鼠标滑过重置消失时间falsetrue、false
animateInside文字动画效果falsetrue开启、false关闭
className自定义class多个用空格分隔
theme主题lightlight、dark
audio音效1,2,3,4,5,6
    
image显示图片图片地址
imageWidth图片宽度60数字
buttons显示按钮[][ [ 'btn1', function(){} ], ['btn2', function(){} ] ]
overlay遮罩层falsetrue显示,false不显示
drag滑动关闭truetrue开启,false关闭
layout布局类型21标题和内容并排,2两排显示
rtl布局方向falsefalse内容居左,true居右
displayMode显示模式00无限制,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>

 

<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就会在点击排序时自动传递sortorder参数,例如: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-showexport-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
optionurl方式的配置 

 如果data是string类型会把data当url请求数据,option是请求的配置,跟表格的配置一样,配置method、where、headers等, 接口返回的格式也要跟表格一样包含code、count、data等信息。

 cols的配置也跟表格一样,是一个多维数组,可以通过insTb3.config.cols来获取表格的cols,也可以重写。

 导出的数据会包含templet转换,如果templet返回的是switch开关、icon图标等,可以通过export-showexport-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;
});

 

 

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参数叠加使用。

 

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()触发打印。

 

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.全部参数

参数类型说明示例
elemString/DOM指定容器的选择器或DOM"#demo"
templetStringitem模板,模板遵循 laytpl 语法 
dataArray直接赋值数据,也可前端分页[{},{},{}]
urlString接口地址 
methodString接口http请求类型,默认:get 
whereObject接口的请求参数where: {id: 123}
headersObject接口的请求头headers: {token: 'sasasas'}
contentTypeString发送到服务端的内容编码类型contentType: 'application/json'
parseDataFunction数据格式解析的回调函数 
requestFunction用于对分页请求的参数重新设定名称默认是page、limit
useAdminBoolean是否使用admin.ajax默认false
doneFunction数据渲染完的回调 
pageObject开启分页,配置分页参数 
loadMoreObject开启加载更多,配置加载更多参数 

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中,如果要自定义主题步骤如下:

  1. 前往 主题生成器 在线定制主题;
  2. 将生成的css放在admin.css里面,也可以像admin.css一样引入,可以直接通过<style>写在页面中,也可以建一个theme-all.css引入, 将所有生成的主题都放在这个css文件里面;
  3. 打开“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反向代理来解决跨域问题:

  1. 打开“nginx/conf/nginx.conf”配置文件
  2. 设置反向代理:
     http {
         server {
             # 加入以下配置,之前的配置全部不要动,这个location是新加入的
             location /api/ {
                 proxy_pass  http://11.11.111.111:8088/; # 这个是后台接口所在的地址
             }
         }
     }
  3. 修改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发送的请求还是会有pagelimit这两个参数,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就可以实现一个简单的单页面应用了。

 

 

 

 

 

 

 

 

 

 

 

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Adger_mi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值