在大型PC应用开发中,多tab和多级菜单的方式比较常用,如果能够记忆用户上次打开的tab和菜单对用户来说是非常便利的,每次打开系统就能看到之前打开的页面继续进行浏览,提升体验和效率,下面我们来看看如何使用浏览器的存储和layuiTab组件实现这个功能。

首先是主要的顶部layui tab组件的结构,tab里面有个iframe容器,主要使用iframe来当做对应页面的视图,类似vueRouter中的<router-view>,每个一个tab内容都有一个视图,这样就能多个视图独立保存状态,不影响其他tab
我们菜单对应页面模块文件结构是一个文件夹一个模块,文件夹名就是模块名,文件夹下的index.html就是对应的页面(模块1/index.html),所以在点击时需要存储当前文件夹的名字就能知道上一次用户打开的模块页面

网站的一级导航结构在这里就不给大家展示了,直接给代码:

   <ul id="main-menu" class="font-optimize">
      <li
        id="53a86ff50bbf443fb1a142fd78483ed7"
        class="homePage_1 sec-menu-item menu-authority-control"
      >
        <a
          href="#"
          onclick="changeUrl('homePage/index.html','homePage_1',this)"
        >
          <p class="fontColor">首页</p>
        </a>
      </li>
      <li
        id="ff09a2cdd1b941329ff480eed06feb7c"
        class="seatManagement_1 sec-menu-item menu-authority-control"
      >
        <a
          href="#"
          onclick="changeUrl('seatManagement/index.html','seatManagement_1',this)"
        >
          <p class="fontColor">工作台</p>
        </a>
      </li>
      <li
        id="84cc08dfaa9047e087ca4104c1e28820"
        class="WorkOrderManagement_1 sec-menu-item menu-authority-control"
      >
        <a
          href="#"
          onclick="changeUrl('WorkOrderManagement/workStation.html','WorkOrderManagement_1',this)"
        >
          <p class="fontColor">工单管理</p>
        </a>
      </li>
      <li
        id="d6d8c3f28cc2427ba13fe26170645960"
        class="BasicSet_1 sec-menu-item menu-authority-control"
      >
        <a
          href="#"
          onclick="changeUrl('BasicSet/index.html','UserManage',this) "
        >
          <p class="fontColor">xxxx</p>
        </a>
      </li>
    </ul>

    <div class="body-main">
      <div
        class="layui-tab active"
        lay-filter="mainTab"
        lay-allowClose="true"
        style="height: 100%"
      >
        <ul class="layui-tab-title">
          <li class="layui-this" lay-id="53a86ff50bbf443fb1a142fd78483ed7">
            首页
          </li>
          <li lay-id="ff09a2cdd1b941329ff480eed06feb7c" class="seatManagement">
            坐席工作台
          </li>
        </ul>
        <div class="layui-tab-content" style="height: calc(100% - 42px)">
          <div class="layui-tab-item layui-show">
            <div class="report-list">
              <iframe
                id="home"
                src="./home/index.html"
                width="100%"
                height="100%"
                scrolling="no"
                marginheight="0"
                marginwidth="0"
              ></iframe>
            </div>
          </div>
          <div class="layui-tab-item" style="height: 100%">

           ...

          </div>
        </div>
      </div>
    </div>

这里我给菜单的唯一id设置了哈希值,大家可以自己定义一个语义化的,只要保持唯一就行

tab部分示例图:


这里是设置首页和工作台的tab不可关闭(隐藏掉关闭按钮)

    .body-main .seatManagement{
      display: none;
    }
    .layui-tab-title > li:first-child .layui-tab-close {
      display: none;
    }
    .layui-tab-title > .seatManagement .layui-tab-close {
      display: none;
    }
    body {
      padding: 0;
    }
    .body-main iframe {
      background-color: #fff;
      border: 0px;
    }
    .body-main {
      width: 100%;
      height: 100%;
    }
    .layui-tab-item {
      height: 100%;
    }

接下来是js逻辑:
changeUrl为菜单跳转的方法

核心逻辑是在用户打开网站的时候利用浏览器的storage和layuitab的element把之前记忆的tab先一个个创建出来,然后通过记忆的activeMenuId切换到上次打开的tab:

  if (sessionStorage.getItem("lastTabList")) {
        //把记忆的tab全部创建出来

        var crrentTabName = $(".layui-tab.active").attr("lay-filter");
        var lastTabList = JSON.parse(sessionStorage.getItem("lastTabList"));
        lastTabList.forEach(function (i) {
          element.tabAdd(crrentTabName, {
            title: i.text,
            content:
              '<iframe  src="./' +
              i.url +
              '" width="100%" height="100%" scrolling="no" marginheight="0" marginwidth="0"></iframe>', //支持传入html
            id: i.id,
          });
        });
      }

然后用户在点击导航的时候,如不存在该tab,也就是用户没有记忆这个菜单的tab,那么我们就根据传入的url创建新的tab并记忆。

如已存在该tab的话就简单,直接tabChange切换过去就好,tab的内容可以是对应iframe视图,视图里可以有二级菜单,二级菜单的记忆是单独的,文章后面会介绍。

大家可能会疑惑tab里对应的页面是怎么记忆的,其实是我们在点击菜单跳转的时候就已经把当前页面的url存储到了storage里面,在下次打开网站时读取记忆并动态添加iframe的方式给显示出来。
注意每次删除和添加新tab的时候都要对storage进行修改(更新一下记忆),添加的时候存储一下当前打开的tab名activeMenuId
完整代码:

    layui.use(["element", "layer"], function () {
      var element = layui.element;
      window.changeUrl = function (url, className, e) {
   $("#main-menu > li").removeClass("selected");
        $("#main-menu > ." + url.split("/")[0] + "_1").addClass("selected");
        sessionStorage.setItem("url", url);
        sessionStorage.setItem("className", className);
   var crrentTabName = $(".layui-tab.active").attr("lay-filter");
        var reportName = $(e).parent().attr("id");
        if (
          url === "homePage/index.html" ||
          url === "seatManagement/index.html"
        ) {
          //如为首页或坐席直接切换tab,默认已有页面
          element.tabChange(crrentTabName, reportName);
          return;
        }
        //记忆当前打开的tab信息
        var lastTabList = JSON.parse(sessionStorage.getItem("lastTabList"));
        var id = $(e).parent().attr("id");
        if (!lastTabList) {
          sessionStorage.setItem(
            "lastTabList",
            JSON.stringify([
              { id: id, text: $(e).find(".fontColor").text(), url: url },
            ])
          );
        } else {
          var arridx = lastTabList.some(function (i) {
            return i.id === id;
          });
          if (!arridx) {
            lastTabList.push({
              id: id,
              text: $(e).find(".fontColor").text(),
              url: url,
            });
          }
          sessionStorage.setItem("lastTabList", JSON.stringify(lastTabList));
        }

        //点击后新增tab
        element.tabDelete(crrentTabName, reportName);
        element.tabAdd(crrentTabName, {
          title: $(e).find(".fontColor").text(),
          content:
            '<iframe  src="./' +
            url +
            '" width="100%" height="100%' +
            '" scrolling="no" marginheight="0" marginwidth="0"></iframe>', //支持传入html
          id: reportName,
        });

        element.tabChange(crrentTabName, reportName);
      };

      //tab切换
      element.on("tab(mainTab)", function (data) {
        if ($(".layui-show iframe").attr("src")) {
          var url = $(".layui-show iframe").attr("src").slice(2);
          sessionStorage.setItem("url", url);
        }
        sessionStorage.setItem("activeMenuId", $(this).attr("lay-id"));
      });
      //tab删除
      element.on("tabDelete(mainTab)", function (data) {
        var delId = $(this).parent().attr("lay-id");
        var lastTabList = JSON.parse(sessionStorage.getItem("lastTabList"));
        lastTabList.forEach(function (i, idx) {
          //删除对于记忆的tab项
          console.log(i.id, delId);
          if (i.id === delId) {
            lastTabList.splice(idx, 1);
          }
        });
        sessionStorage.setItem("lastTabList", JSON.stringify(lastTabList));
      });

      if (sessionStorage.getItem("lastTabList")) {
        //把记忆的tab全部创建出来

        var crrentTabName = $(".layui-tab.active").attr("lay-filter");
        var lastTabList = JSON.parse(sessionStorage.getItem("lastTabList"));
        lastTabList.forEach(function (i) {
          element.tabAdd(crrentTabName, {
            title: i.text,
            content:
              '<iframe  src="./' +
              i.url +
              '" width="100%" height="100%" scrolling="no" marginheight="0" marginwidth="0"></iframe>', //支持传入html
            id: i.id,
          });
        });
      }

      element.tabChange(
        $(".layui-tab.active").attr("lay-filter"),
        sessionStorage.getItem("activeMenuId")
      );
    });

    if (sessionStorage.getItem("activeMenuId")) {
      $("#main-menu > #" + sessionStorage.getItem("activeMenuId")).addClass(
        "selected"
      ); //高亮一级
    } else {
      $(".homePage_1").addClass("selected");
    }


        //页面加载完成后调整主体高度
                $(".body-main").css(
                  "height",
                  "calc(100% - " +
                    ($(".main-top").outerHeight() +
                      $(".main-header").outerHeight() +
                      30) +
                    "px)"
                );
                $("#home").height(
                  $(".body-main").height() -
                    $(".body-main .layui-tab-title").outerHeight()
                );

剩下其余的就是一些样式调整和默认selected

storage的存储情况:


以下是二级菜单的结构,使用layui的导航组件为例:

<body>
  <div class="body-left">
    <div class="left-nav">
      <ul class="layui-nav layui-nav-tree layui-inline " lay-filter="leftnav">
            <li id="f21c3279e67448d1a5a96d376b3695f6" class="layui-nav-item  UserManage" ><a href="#" onclick="changeUrlSec('BasicSet/UserManage/index.html', 'UserManage')"> 用户管理</a></li>
                    <li id="4145cd3cde9740bb9bec57ec9bfba31a" class="layui-nav-item  UserAuthorization" ><a href="#" onclick="changeUrlSec('BasicSet/UserAuthorization/index.html', 'UserAuthorization')">用户角色</a></li>

          <li  class="layui-nav-item version" ><a href="#" onclick="changeUrlSec('BasicSet/index.html', 'version')">版本更新</a></li>
      </ul>
    </div>
  </div>
       <div class="left-button layui-icon layui-icon-left" ></div>
  <div class="body-main">
    <iframe id="Iframe"  width="100%" height="100%" scrolling="no"
    marginheight="0" src="./UserManage/index.html" marginwidth="0"></iframe>
  </div>
</body>

这里的话就比较简单,存储对应二级页面的文件夹名,然后在下次打开时直接设置视图的src对记忆页面进行显示。


  window.changeUrlSec = function (url, className) {
    var currtMenu = url.slice(0, url.indexOf("/")) + "/";
    if (window.location.pathname.indexOf(currtMenu) > -1) {
      sessionStorage.setItem("className", className);
    }
    var ss = window.location.href.indexOf("/html/");
    var aa = window.location.href.substr(0, ss + 6);
    url = aa + url.split("/")[0];
    console.log(url);
    document.getElementById("Iframe") &&
      (document.getElementById("Iframe").src =
        url + "/" + className + "/index.html"); //跳转动作
  };

  var url = sessionStorage.getItem("url");
  var currtMenu = url.slice(0, url.indexOf("/")) + "/";
  if (
    sessionStorage.getItem("className") !== "null" &&
    window.location.pathname.indexOf(currtMenu) > -1 //与记忆的url符合才进行跳转
  ) {
    //记忆上次停留菜单
    $("." + sessionStorage.getItem("className")).addClass("layui-this");
    if (url) {
      changeUrlSec(url, sessionStorage.getItem("className"));
    }
  } else {
    //如上次菜单不匹配则默认第一个选中
    $(".layui-nav > .layui-nav-item:first-child").addClass("layui-this");
  }
});

【手摸手教你实现移动端滚动记忆(带分页)】https://segmentfault.com/a/11...

03-05 15:48