zl程序教程

您现在的位置是:首页 >  Java

当前栏目

LayUI之旅-入门

2023-02-18 16:29:48 时间

最近要做一个项目,被要求前端要使用layui,甲方爸爸很牛逼的好吗,既然要求这样了,二话不说,撸起袖子就开干,由于从来没用过layui这个框架,对框架的不熟悉导致在使用的过程中是步步都是障碍啊,还是那句话“好记性不如烂笔头”,那就记录一下都遇到了些什么问题以及一些用法吧。

首先呢,引入这个框架,很简单,直接去官网下载,LAYUI官网地址:https://www.layui.com,官方文档:https://www.layui.com/doc

然后在合适的位置引入框架即可,下面就是最简单的引入方式了:

<link rel="stylesheet" href="/layui/css/layui.css">
<script src="/layui/layui.js"></script>

再来看看后台经典三栏式布局

<body class="layui-layout-body" layadmin-themealias="default">
    <div class="layadmin-tabspage-none">
        <div class="layui-layout layui-layout-admin">
            <!-- 头部区域(可配合layui已有的水平导航) -->
            <div class="header layui-header"></div>
            <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
            <div class="left layui-side layui-side-menu "></div>
            <!-- 内容主体区域 -->
            <div class="right layui-body"></div>
            <!--  用于移动设备下遮罩  -->
            <div class="layadmin-body-shade" layadmin-event="shade" display="none"></div>
            <!-- 底部固定区域 -->
            <div class="footer layui-footer">
                &copy;&nbsp;xxx.com
            </div>
        </div>
    </div>
</body>  

嗯,一切都很顺利,看着蛮舒服的嘛,但是问题也接踵而至。

1、实现侧边栏显示与隐藏

看官网的后台演示模板(layAdmin),怎么看都比自己这个舒服啊,首先,左边栏是可以隐藏的,按照官方的演示模板,添加了一个按钮,用来显示和隐藏侧边栏(这里需要说明一下,就目前的网页设计要求,不仅仅要PC端使用,还有移动端也是要使用的,所以需要实现左边栏的显示和隐藏),因为设计是右侧(页面内容区域)异步加载(这是最终确定的方案),所以页面上的所有事件的绑定都需要用事件委托来处理(刚开始我也没注意到这个问题,导致后面遇到了其他问题)。

代码很快就写完了,然后进行效果测试,诶···怎么怪怪的,左边栏隐藏和显示实现了啊,但是头部的logo没有隐藏啊,然后不断的查看layui的源码(开启扒站模式),发现要完美呈现官网的layAdmin的侧边栏隐藏效果是需要重新写css的,又重新搞了一下admin.css(重要:需要在外部容器的classname增加“layui-layout-admin”否则admin.css不生效),引入了新的CSS,再看源码是通过修改classname来实现的,于是开始改改改,最后改完了就是下面这个样子的啦。

    /*
     * @todo 左侧导航菜单的显示和隐藏
     */
    $('.header').on('click', '.layadmin-flexible', function(event) {
        if (device.ios || device.android || dwidth) {
            //mobile 移动端用到的classname是“layadmin-side-spread-sm”
            $('.layadmin-tabspage-none').toggleClass("layadmin-side-spread-sm");
            $('.layadmin-body-shade').toggle();//隐藏移动端遮罩层
        }else{
            //PC PC上并不是直接隐藏而是变成了图标的小导航,用到的classname是“layadmin-side-shrink”
            $('.layadmin-tabspage-none').toggleClass("layadmin-side-shrink");
        }
        $('.layadmin-flexible i').toggleClass('layui-icon-shrink-right');
        $('.layadmin-flexible i').toggleClass('layui-icon-spread-left');
    });
    //点击遮罩背景,隐藏左侧菜单和遮罩自身
    $('.layadmin-body-shade').click(function(event) {
        if (device.ios || device.android || dwidth) {
            //移动端
            $('.layadmin-tabspage-none').toggleClass("layadmin-side-spread-sm");
        }else{
            //PC端
            $('.layadmin-tabspage-none').toggleClass("layadmin-side-shrink");
        }
        $(this).hide();
    });  

看到了吧,所有的实现就是这么简单,因为官方没有在文档里面写这些,鼓捣出来这么个结果也是花了一些时间,挺佩服我自己的呢!

注意:上面的代码用到了device模块,在使用之前必须先加载(use),详细的可以参照官方的加载所需模块

2、实现右侧内容部分的异步加载(局部刷新)

刚开始想到的是直接用html的iframe来实现,很快就实现了,而且用起来还算正常,但被否定了,因为iframe一旦多起来之后,浏览器的开销就相当大,这样对于手机端是很不利的,所以毅然决然的毙了这种方案,代码如下:

HTML部分

<div class="main layui-tab" lay-allowclose="true" lay-filter="frame" style="padding: 15px;">
    <ul class="layui-tab-title"></ul>
    <div class="layui-tab-content"></div>
</div>
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<div class="left layui-side layui-side-menu ">
    <div class="layui-side-scroll">
        <div class="layui-logo" lay-href=""><span>layui</span></div>
        <ul class="layui-nav layui-nav-tree" lay-filter="menu">
            <li class="layui-nav-item layui-nav-itemed">
                <a lay-href="/index/index/no1" data-id="1" data-title="菜单1" data-type="tabAdd">
                    <i class="layui-icon layui-icon-dollar"></i>
                    <cite>菜单1</cite>
                </a>
            </li>
            <li class="layui-nav-item layui-nav-itemed">
                <a lay-href="/index/index/no2" data-id="2" data-title="菜单2" data-type="tabAdd">
                    <i class="layui-icon layui-icon-read"></i>
                    <cite>菜单2</cite>
                </a>
            </li>
        </ul>
    </div>
</div> 

JS部分

// 配置tab实践在下面无法获取到菜单元素
    $('body').on('click',".layui-nav-itemed a", function () {
        var dataid = $(this);
        //这时会判断右侧.layui-tab-title属性下的有lay-id属性的li的数目,即已经打开的tab项数目
        if ($(".layui-tab-title li[lay-id]").length <= 0) {
            //如果比零小,则直接打开新的tab项
            active.tabAdd(dataid.attr("lay-href"), dataid.attr("data-id"), dataid.attr("data-title"));
        } else {
            //否则判断该tab项是否以及存在
            var isData = false; //初始化一个标志,为false说明未打开该tab项 为true则说明已有
            $.each($(".layui-tab-title li[lay-id]"), function () {
                //如果点击左侧菜单栏所传入的id 在右侧tab项中的lay-id属性可以找到,则说明该tab项已经打开
                if ($(this).attr("lay-id") == dataid.attr("data-id")) {
                    isData = true;
                }
            })
            if (isData == false) {
                //标志为false 新增一个tab项
                active.tabAdd(dataid.attr("lay-href"), dataid.attr("data-id"), dataid.attr("data-title"));
            }
        }
        //最后不管是否新增tab,最后都转到要打开的选项页面上
        active.tabChange(dataid.attr("data-id"));
    });

    var active = {
        //在这里给active绑定几项事件,后面可通过active调用这些事件
        tabAdd:function (url, id, name) {
            //新增一个Tab项 传入三个参数,分别对应其标题,tab页面的地址,还有一个规定的id,是标签中data-id的属性值
            //关于tabAdd的方法所传入的参数可看layui的开发文档中基础方法部分
            element.tabAdd('frame', {
                title:name,
                content:'<iframe data-frameid="' + id + '" scrolling="auto" frameborder="0" src="' + url + '" style="width:100%;height:99%;"></iframe>',
                id:id //规定好的id
            })
            FrameWH();  //计算ifram层的大小
        },
        tabChange:function (id) {
            //切换到指定Tab项
            element.tabChange('frame', id); //根据传入的id传入到指定的tab项
        },
        tabDelete:function (id) {
            element.tabDelete("frame", id);//删除
        }
    };
    function FrameWH() {
        var h = $(window).height();
        $("iframe").css("height",h+"px");
    }

那么,既然iframe的方案被毙了,就需要有新的方案来实现,有两种方案可行;

第一种,每一页都独立加载,使用框架的模板继承来实现头部和左边导航栏的重载(原样输出),这样有一个问题,就是用户不知道自己刚刚点击的是导航栏的哪一项,以及重复验证用户是否登录,这需要借助cookie和session来实现,最关键的问题在于点击后页面会有一次跳转,用户体验不太好,不是首选。

第二种,头部和左边栏固定,右侧内容区域使用异步加载,ajax去向后台取内容,并进行局部刷新,这个方案很符合要求,所以这次的项目也是采用这个方案来实现的。

已经确定列实现方案,立马开始实现左边栏的点击功能

HTML部分

<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<div class="left layui-side layui-side-menu ">
    <div class="layui-side-scroll">
        <div class="layui-logo" lay-href="">
            <span>layui</span>
        </div>
        <ul class="layui-nav layui-nav-tree" lay-filter="menu">
            <li class="layui-nav-item layui-nav-itemed">
                <a lay-href="/index/index/no1" data-id="1" data-title="菜单1" data-type="tabAdd">
                    <i class="layui-icon layui-icon-dollar"></i>
                    <cite>菜单1</cite>
                </a>
            </li>
            <li class="layui-nav-item layui-nav-itemed">
                <a lay-href="/index/index/no2" data-id="2" data-title="菜单2" data-type="tabAdd">
                    <i class="layui-icon layui-icon-read"></i>
                    <cite>菜单2</cite>
                </a>
            </li>
        </ul>
    </div>
</div>

JS部分

    /*
     * @todo 左侧菜单事件,示例中菜单只有一级,多级菜单看情况修改就好
     */
    $('.layui-side-menu').on('click', '.layui-nav-itemed', function(event) {
        var url = $(this).children('a').attr('lay-href'),
            title = $(this).find('cite').html(),
            index = $('.left-nav #nav li').index($(this));
        $.ajax({
            "type":"GET",
            "url":url,
            "dataType":"text",
            "success":function(data){
                data = JSON.parse(data);//解析后台返回的json,如果你的后台返回的是text,这步可以忽略
                $('.main').empty();
                $(".main").html(data);
                $('.layadmin-tabspage-none').removeClass("layadmin-side-spread-sm");//隐藏左侧菜单Mobile
                $('.layadmin-tabspage-none').removeClass("layadmin-side-shrink");//隐藏左侧菜单PC
                $('.layadmin-body-shade').hide();//隐藏遮罩
            }
        });
    });

3、异步加载的页面内容中的按钮点击无效

这个也怪自己没有经验,解决方法很简单,直接把事件委托到祖先元素上(这个元素必须是首页模板里面就存在的,也就是非异步加载的元素,否则绑定失败),我这里用的是JQ的 on() 方法,on() 方法自JQuery1.7引入,是 bind()、live() 和 delegate() 的替代品。这里使用 on() 方法是因为她添加的事件处理程序适用于当前及未来的元素。

提示:移除事件,使用 off() 方法。

提示:添加只运行一次的事件然后移除,使用 one() 方法。

语法:$(selector).on(event,childSelector,data,function)

参数

描述

event

必需。规定要从被选元素移除的一个或多个事件或命名空间。 由空格分隔多个事件值,也可以是数组。必须是有效的事件。

childSelector

可选。规定只能添加到指定的子元素上的事件处理程序(且不是选择器本身,比如已废弃的 delegate() 方法)。

data

可选。规定传递到函数的额外数据。

function

可选。规定当事件发生时运行的函数。

示例代码如下:

$(".layui-body").on("click",".flowTable .search-btn",function (){
    //some js code...
 });

4、异步加载的页面内容中的事件被重复执行

上面说到了,要使异步加载的页面内容的事件生效,需要进行事件委托,但我在委托完毕之后发现事件会被重复执行,表现的现象是:第一次点击,执行一次;第二次点击,执行两次;第N次点击,执行N次,这个问题很严重,造成服务器请求次数过多时“小事”,因为事件被重复执行,部分页面功能无法按既定目标完成执行才是“大事”。

举个例子:index是固定内容,当点击index里面的“菜单1”之后异步加载“页面1”的内容,这时如果事件委托写在“页面1”中,事件就会被重复执行。

解决方法:将事件委托写在外面,也就是写在上面这个例子的index中。

为此,我新扩展了一个admin模块,admin.js:

layui.define(["jquery","table","form","layer","util","element"],function(exports){
    var $ = layui.jquery,
        table = layui.table,
        form = layui.form,
        layer = layui.layer,
        util = layui.util,
        element = layui.element,
        device = layui.device();
    exports('admin', {}); //模块输出的核心,这句是必须的
});

要加载自定义模块,方法如下(写在index中):

方法:layui.define([mods], callback),[mods]模块依赖,callback回调函数

layui.config({
    base: '/js/',
    version: '1.1.2'
}).use('admin');

因为扩展了模块,所以干脆把所有的东西都搬到这个admin.js来了,图个方便

5、数据表格

先记下官方文档地址:https://www.layui.com/doc/modules/table.html

我这里用的基本方法渲染,也就是 table.render() 方法,本应该用下面这样的方法来实现

HTM L部分

<table id="demo" lay-filter="test"></table>

JS部分

  table.render({
    elem: '#demo'  //指定原始表格元素选择器(推荐id选择器)
    ,height: 312  //容器高度
    ,url: '/demo/table/user/' //数据接口
    ,page: true //开启分页
    ,cols: [[ //表头
      {field: 'id', title: 'ID', width:80, sort: true, fixed: 'left'}
      ,{field: 'username', title: '用户名', width:80}
      ,{field: 'sex', title: '性别', width:80, sort: true}
      ,{field: 'city', title: '城市', width:80} 
      ,{field: 'sign', title: '签名', width: 177}
      ,{field: 'experience', title: '积分', width: 80, sort: true}
      ,{field: 'score', title: '评分', width: 80, sort: true}
      ,{field: 'classify', title: '职业', width: 80}
      ,{field: 'wealth', title: '财富', width: 135, sort: true}
    ]]
  });

但因为页面需求的问题,需要在同一个页面加载不同的表格(点击某个按钮之后)将这个按钮对应的表格渲染出来(也就是异步的)因为前面对框架不熟悉,使用传递已知数据的方法进行了渲染

table.render({
    elem: '#table',
    cellMinWidth: 80, //全局定义常规单元格的最小宽度,layui 2.2.1 新增
    page: true,
    cols: cols,//格式是这样 [[{},{},{}]]
    data: data //这里的data是ajax异步取到的数据,已经严格按照layui的格式在后端进行了数据重组,所以前端这里可以直接使用
});

直到这里其实都还算顺利,官方文档写的还算详细,虽然有那么一点点乱吧。

坑从何来,因为突然收到一个需求的变化,就是当点击按钮时,渲染的数据表格只是数据集中的一部分(因为数据确实有些多),要查看没一条信息对应的详细情况,需要在表格最右侧增加一列操作列,里面放的是按钮,点这个按钮的时候需要给弹窗出来,然后在弹窗内再展示一个子表。

这个坑呢,主要是因为官方文档是真的有那么一点点乱,按照正常逻辑,是不是应该先讲怎么使用行内工具条,再讲怎么监听工具条事件吧,而官方文档写的尽然是监听头部工具栏事件(然后在下面写了个具体用法参见:绑定工具条,最关键不是这个,而是目录里面没有这一项,^哭唧唧^),搞得我一度认为LAYUI怎么如此不成熟,连行内工具条都没提供…

说明:要使用工具条,先得有工具条的模版(写在body中就可以)

<script type="text/html" id="barDemo">
  <a class="layui-btn layui-btn-xs" lay-event="detail">查看</a>
  <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
  <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
 
  <!-- 这里支持 laytpl 语法,如: -->
  {{#  if(d.auth > 2){ }}
    <a class="layui-btn layui-btn-xs" lay-event="check">审核</a>
  {{#  } }}
</script>
 
注意:属性 lay-event="" 是模板的关键所在,值可随意定义。

我没有用layui的模版引擎,所以并不会用,这里是laytpl模版引擎文档:https://www.layui.com/doc/modules/laytpl.html

然后在配置列(cols)的时候增加下面这一条就可以啦

{fixed:'right', title:'操作', align:'center', toolbar:'#barDemo', width:150}//这里的toolbar值是模板元素的选择器

然后就是监听工具条事件,偷个懒,直接把官方的例子贴过来,这个比较清楚

//监听工具条
table.on('tool(test)', function(obj){ //注:tool是工具条事件名,test是table原始容器的属性 lay-filter="对应的值"
  var data = obj.data; //获得当前行数据
  var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值)
  var tr = obj.tr; //获得当前行 tr 的DOM对象
 
  if(layEvent === 'detail'){ //查看
    //do somehing
  } else if(layEvent === 'del'){ //删除
    layer.confirm('真的删除行么', function(index){
      obj.del(); //删除对应行(tr)的DOM结构,并更新缓存
      layer.close(index);
      //向服务端发送删除指令
    });
  } else if(layEvent === 'edit'){ //编辑
    //do something
    
    //同步更新缓存对应的值
    obj.update({
      username: '123'
      ,title: 'xxx'
    });
  }
});

6、layer.open()弹窗的使用

弹窗如果只是要给用户一个提示这样就很简单,官方文档里面写的比较详细,一个简单的例子如下:

layer.msg('hello');//弹出一个消息,停留一段时间后消失

layer.open({
  title: '在线调试'  //标题
  ,content: '可以填写任意的layer代码'  //可以是文本,也可以是DOM
}); 

刚刚提到了,我是要在弹窗里面再渲染一个表格,那怎么实现呢?官方文档里面一个字都没提到过这种形式(可以理解,写文档的人也不可能知道所有程序员的需求,就跟程序员永远也不清楚产品经理的需求一样),因为这个需求,反复的把弹出层和数据表格的文档啃了一遍又一遍,终于,被我发现了这么一个东西

layer.open({
  content: '测试回调',
  success: function(layero, index){
    console.log(layero, index);
  }
});

对,没错,就是弹出层open后的success回调,那么我就可以把table.render()方法写在这个回调里面啦,这样不就能实现了么,于是踩到了这个坑。

前面提到过,渲染表格,是需要一个已经存在的DOM的,但是弹出层是未来元素,并不是已经存在的元素,是不可控的,那就得再写一个table,然后js写成了这样

layer.open({
    title: '详情查看',
    content: $("#view-details-div"),//这里content是一个DOM,注意:最好该元素要存放在body最外层,否则可能被其它的相对元素所影响
    success: function(layero, index){
        table.render({
            elem: "#details",
            cellMinWidth: 80, //全局定义常规单元格的最小宽度,layui 2.2.1 新增
            cols: [[
                {field:'0', title:'姓名', align:'center'},
                {field:'1', title:'语文', align:'center'},
                {field:'2', title:'数学', align:'center'},
                {field:'3', title:'英语', align:'center'},
                {field:'4', title:'物理', align:'center'},
                {field:'5', title:'化学', align:'center'}
            ]],
            data: data.details
        });
    }
});

在PC上的效果还是不错的(后来发现弹出层只会出现一次,再次点按钮时就没有反应了),到了手机上,表格展示不完整(对,就是没有自适应),而且弹窗无法移动,继续啃文档,查资料(资料是真的很少),然后不停的改,不停的调,终于找到了解决方案

layer.open({
    type: 1,//弹出层open之后,content引用的DOM会被销毁,如果不写这句,type默认为0,DOM不会被还原,所以只有第一次点击才能有效弹出
    title: '详情查看',
    area: '100%',//弹出层宽高,这里只设置了宽度,这样就能在双端自适应了
    btn: '我知道了',//按钮文本,只有一个按钮时是字符串,多个按钮时是数组
    btnAlign: 'c',//按钮居中
    shade: 0,//不显示遮罩层
    content: $("#view-details-div"),//这里content是一个DOM,注意:最好该元素要存放在body最外层,否则可能被其它的相对元素所影响
    success: function(layero, index){
        table.render({
            elem: "#details",
            cellMinWidth: 80, //全局定义常规单元格的最小宽度,layui 2.2.1 新增
            cols: [[
                {field:'0', title:'姓名', align:'center'},
                {field:'1', title:'语文', align:'center'},
                {field:'2', title:'数学', align:'center'},
                {field:'3', title:'英语', align:'center'},
                {field:'4', title:'物理', align:'center'},
                {field:'5', title:'化学', align:'center'}
            ]],
            data: data.details
        });
    }
});

LayUI: 1 / 2

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息: 原文出处:Yiiven https://www.yiiven.cn/layui-trip.html