博客列表 >2.【商城后台管理系统】基于TP6开发后台管理界面渲染与无限级菜单查询

2.【商城后台管理系统】基于TP6开发后台管理界面渲染与无限级菜单查询

 一纸荒凉* Armani
 一纸荒凉* Armani原创
2021年06月16日 11:12:551925浏览

【商城后台管理系统】基于TP6开发后台管理界面渲染与无限级菜单查询

预览地址:http://padmin.easys.ltd/admin/account/login

后台管理页面效果预览

界面部署流程

  • 页眉栏:采用layui 50px 经典蓝 自设置用户信息Session 进行输出用户名称和用户级别
  • 左侧导航栏{无限级菜单} :采用layui 手风琴和左侧导航框架进行样式设计,通过连接后台数据库导出数据表中数据渲染导航列表,分为一级菜单和二级菜单
  • 主操作区:采用传统 iframe 内联框架进行部署
  • 主操作区高度设置:采用css position 定位以及页眉和左部导航还有页面高度进行计算部署

一、后台搭建

新建控制器Home.php

  1. <?php
  2. namespace app\admin\controller;
  3. use app\BaseController;
  4. use think\facade\Session;
  5. use think\facade\View;
  6. use think\facade\Db;
  7. use think\facade\Cache;
  8. use think\facade\Request;
  9. /**
  10. * 后台首页
  11. */
  12. class Home
  13. {
  14. public function index(){
  15. // 用户信息
  16. $data['admin'] = Session::get('admin');
  17. // 获取继承父类Base的属性
  18. // 用户角色(根据用户信息的角色gid查询对应的角色名称)
  19. $data['group'] = Db::table('admin_group')->where('id',$data['admin']['gid'])->find();
  20. View::assign([
  21. 'data'=>$data
  22. ]);
  23. return View::fetch('/home/index');
  24. }
  25. public function welcome(){
  26. return View::fetch('/home/welcome');
  27. }
  28. }

新建后台主体页面视图 Home/index.php

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title></title>
  5. <link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css">
  6. <script type="text/javascript" src="/static/layui/layui.js"></script>
  7. <style type="text/css">
  8. body{padding:0;margin: 0;}
  9. .myheader{background: #009688;height: 50px;padding: 0 10px;line-height: 50px;color: #fff;display: flex;justify-content: space-between;}
  10. .myheader .title .logo{width: 30px;vertical-align: sub;border-radius: 8px;margin-right: 12px;}
  11. .myheader .title{font-size: 18px;cursor: pointer;}
  12. .admin-info a{color: #fff;margin-left:6px;}
  13. .main-menus{width: 200px;position:absolute;height: calc(100% - 50px)}
  14. .main-menus .layui-inline{height: 100%;overflow: auto;}
  15. .main-contain{position: absolute;left: 200px;right: 0;height: calc(100% - 50px);}
  16. .main-contain iframe{width: 100%;height: 100%;background: #ececec;}
  17. /* 设置滚动条的样式 */
  18. ::-webkit-scrollbar {
  19. width:12px;
  20. }
  21. /* 滚动槽 */
  22. ::-webkit-scrollbar-track {
  23. -webkit-box-shadow:inset006pxrgba(0,0,0,0.3);
  24. border-radius:10px;
  25. }
  26. /* 滚动条滑块 */
  27. ::-webkit-scrollbar-thumb {
  28. border-radius:10px;
  29. background:rgba(0,0,0,0.1);
  30. -webkit-box-shadow:inset006pxrgba(0,0,0,0.5);
  31. }
  32. ::-webkit-scrollbar-thumb:window-inactive {
  33. background:rgba(255,0,0,0.4);
  34. }
  35. </style>
  36. </head>
  37. <body>
  38. <!-- 顶部提示条 -->
  39. <div class="myheader">
  40. <span class="title" onclick="goIndex()"><img src="/favicon.ico" class="logo">商城后台管理系统</span>
  41. <span class="admin-info">
  42. <i class="layui-icon layui-icon-user"></i> {$data['admin']['username']}【{$data['group']['title']}】
  43. <a href="javascript:;" onclick="logout()"><i class="layui-icon layui-icon-logout"></i> 退出</a>
  44. </span>
  45. </div>
  46. <!-- 侧边导航菜单 -->
  47. <div class="main-menus">
  48. </div>
  49. <!-- 主操作内容区域 -->
  50. <div class="main-contain">
  51. <iframe src="/admin/home/welcome" frameborder="0"></iframe>
  52. </div>
  53. </body>
  54. </html>
  55. <script>
  56. $ = layui.jquery;
  57. /*function reset_height(obj){
  58. // 当前页面高度-顶部信息条高度 = 主体区域高度
  59. var height = (document.documentElement.clientHeight)-$('.myheader').height();
  60. // 设置主体区域的高度
  61. $(obj).parent('div').height(height);
  62. }*/
  63. // 返回首页
  64. function goIndex(){
  65. window.location.href = '/';
  66. }
  67. // 退出登陆
  68. function logout(){
  69. layer.confirm('确定要退出登陆吗?',{
  70. icon:3,
  71. btn: ['确定','取消']
  72. },function(){
  73. $.get('admin/account/logout',function(res){
  74. layer.msg(res.msg,{icon:1});
  75. setTimeout(function(){
  76. window.location.href = "/admin/home/login";
  77. },1000)
  78. },'json');
  79. });
  80. }
  81. </script>

欢迎页面视图文件 Home/welcom.php

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title></title>
  5. </head>
  6. <body style="background: url(http://api.easys.ltd/api/api/api.php);">
  7. <div style="width:100%;height:360px;background: rgba(0,0,0,.6);margin:auto;position: absolute;top: 0;left: 0;right: 0;bottom: 0;">
  8. <p style="text-align:center;line-height: 360px;margin:0;color: #fff;font-size: 32px;">
  9. <span style="font-size: 120px;vertical-align: bottom;"></span>
  10. 欢迎使用phpAdmin后台管理系统 <br>
  11. </p>
  12. <p style="text-align: center;color: #03a9f4;font-size: 24px;position: absolute;bottom: 0;left: 0;right: 0;">当前时间:<span id="curTime"></span> </p>
  13. </div>
  14. </body>
  15. </html>
  16. <script type="text/javascript">
  17. /**
  18. * 如:需求日期格式为:2018-08-28 星期二 21:53:40
  19. * $timeWrapper:dom容器
  20. */
  21. var timeWrapper = document.querySelector('#curTime');
  22. timeWrapper.innerHTML = getCurTime();
  23. setInterval(()=>{
  24. timeWrapper.innerHTML = getCurTime();
  25. }, 1000);
  26. function getCurTime() {
  27. var oDate = new Date();
  28. var weekArr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
  29. var y = oDate.getFullYear(),
  30. m = String(oDate.getMonth()+ 1).padStart(2,0),
  31. d = String(oDate.getDate()).padStart(2,0),
  32. hour = String(oDate.getHours()).padStart(2,0),
  33. min = String(oDate.getMinutes()).padStart(2,0),
  34. sec = String(oDate.getSeconds()).padStart(2,0),
  35. weekIndex = oDate.getDay(),
  36. week = weekArr[weekIndex];
  37. return y + '-' + m + '-' + d + ' ' + week + ' ' + hour + ':' + min + ':' + sec;
  38. }
  39. </script>

二、菜单处理

  1. <?php
  2. namespace app\admin\controller;
  3. use app\BaseController;
  4. use think\facade\Session;
  5. use think\facade\View;
  6. use think\facade\Db;
  7. use think\facade\Cache;
  8. use think\facade\Request;
  9. /**
  10. * 后台首页
  11. */
  12. class Home
  13. {
  14. public function index(){
  15. // 用户信息
  16. // $data['admin'] = Session::get('admin');
  17. // 获取继承父类Base的属性
  18. $data['admin'] = $this->admin;
  19. // 用户角色(根据用户信息的角色gid查询对应的角色名称)
  20. // $data['group'] = Db::table('admin_group')->where('id',$data['admin']['gid'])->find();
  21. $data['group'] = $this->mygroup;
  22. //方案一:直接读数据库 根据一级菜单循环读取二级菜单
  23. // 查询一级菜单(pid==0)
  24. $data['menuList'] = Db::table('admin_menu')->where([['pid','=','0'],['status','=','0']])->select()->toArray();
  25. // 根据一级菜单mid 查询对应的pid二级菜单
  26. foreach ($data['menuList'] as $key => $menu) {
  27. $data['menuList'][$key]['children'] = Db::table('admin_menu')->where('pid','=',$menu['mid'])->select();
  28. }
  29. View::assign([
  30. 'data'=>$data
  31. ]);
  32. return View::fetch('/home/index');
  33. }
  34. public function welcome(){
  35. return View::fetch('/home/welcome');
  36. }
  37. public function logout(){
  38. // 删除session中admin
  39. Session::delete('admin');
  40. echo (json_encode(['code'=>0,'msg'=>'退出成功~']));
  41. }
  42. }

三、菜单渲染与数据优化

视图中循环渲染无限极菜单

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title></title>
  5. <link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css">
  6. <script type="text/javascript" src="/static/layui/layui.js"></script>
  7. <style type="text/css">
  8. body{padding:0;margin: 0;}
  9. .myheader{background: #009688;height: 50px;padding: 0 10px;line-height: 50px;color: #fff;display: flex;justify-content: space-between;}
  10. .myheader .title .logo{width: 30px;vertical-align: sub;border-radius: 8px;margin-right: 12px;}
  11. .myheader .title{font-size: 18px;cursor: pointer;}
  12. .admin-info a{color: #fff;margin-left:6px;}
  13. .main-menus{width: 200px;position:absolute;height: calc(100% - 50px)}
  14. .main-menus .layui-inline{height: 100%;overflow: auto;}
  15. .main-contain{position: absolute;left: 200px;right: 0;height: calc(100% - 50px);}
  16. .main-contain iframe{width: 100%;height: 100%;background: #ececec;}
  17. /* 设置滚动条的样式 */
  18. ::-webkit-scrollbar {
  19. width:12px;
  20. }
  21. /* 滚动槽 */
  22. ::-webkit-scrollbar-track {
  23. -webkit-box-shadow:inset006pxrgba(0,0,0,0.3);
  24. border-radius:10px;
  25. }
  26. /* 滚动条滑块 */
  27. ::-webkit-scrollbar-thumb {
  28. border-radius:10px;
  29. background:rgba(0,0,0,0.1);
  30. -webkit-box-shadow:inset006pxrgba(0,0,0,0.5);
  31. }
  32. ::-webkit-scrollbar-thumb:window-inactive {
  33. background:rgba(255,0,0,0.4);
  34. }
  35. </style>
  36. </head>
  37. <body>
  38. <!-- 顶部提示条 -->
  39. <div class="myheader">
  40. <span class="title" onclick="goIndex()"><img src="/favicon.ico" class="logo">商城后台管理系统</span>
  41. <span class="admin-info">
  42. <i class="layui-icon layui-icon-user"></i> {$data['admin']['username']}【{$data['group']['title']}】
  43. <a href="javascript:;" onclick="logout()"><i class="layui-icon layui-icon-logout"></i> 退出</a>
  44. </span>
  45. </div>
  46. <!-- 侧边导航菜单 -->
  47. <div class="main-menus">
  48. <ul class="layui-nav layui-nav-tree layui-inline layui-bg-cyan">
  49. {foreach $data['menuList'] as $k=>$menu}
  50. <li class="layui-nav-item {if($k==0)}layui-nav-itemed{/if}">
  51. <a href="javascript:;"><i class="layui-icon {$menu['icon']}"></i>&nbsp;&nbsp;{$menu['title']}</a>
  52. {if $menu['children']}
  53. <dl class="layui-nav-child">
  54. {foreach $menu['children'] as $k=>$chd}
  55. <dd><a href="javascript:;" onclick="firemenu(this)" controller="{$chd['controller']}" action="{$chd['action']}">{$chd['title']}</a><dd>
  56. {/foreach}
  57. </dl>
  58. {/if}
  59. </li>
  60. {/foreach}
  61. </ul>
  62. </div>
  63. <!-- 主操作内容区域 -->
  64. <div class="main-contain">
  65. <iframe src="/admin/home/welcome" frameborder="0"></iframe>
  66. </div>
  67. </body>
  68. </html>
  69. <script>
  70. $ = layui.jquery;
  71. /*function reset_height(obj){
  72. // 当前页面高度-顶部信息条高度 = 主体区域高度
  73. var height = (document.documentElement.clientHeight)-$('.myheader').height();
  74. // 设置主体区域的高度
  75. $(obj).parent('div').height(height);
  76. }*/
  77. // 返回首页
  78. function goIndex(){
  79. window.location.href = '/';
  80. }
  81. // 退出登陆
  82. function logout(){
  83. layer.confirm('确定要退出登陆吗?',{
  84. icon:3,
  85. btn: ['确定','取消']
  86. },function(){
  87. $.get('admin/account/logout',function(res){
  88. layer.msg(res.msg,{icon:1});
  89. setTimeout(function(){
  90. window.location.href = "/admin/account/login";
  91. },1000)
  92. },'json');
  93. });
  94. }
  95. </script>

优化无限极菜单查询操作,避免频繁的从数据库中查询操作,我们将第一次查询出来的菜单数据进行文件保存处理、Cacheh缓存处理、分别查询出所有一级和二级菜单,在分别遍历循环处理等优化方案。

  1. <?php
  2. namespace app\admin\controller;
  3. use app\BaseController;
  4. use think\facade\Session;
  5. use think\facade\View;
  6. use think\facade\Db;
  7. use think\facade\Cache;
  8. use think\facade\Request;
  9. use app\admin\controller\Base;
  10. /**
  11. * 后台首页
  12. */
  13. class Home extends Base
  14. {
  15. public function index(){
  16. // 用户信息
  17. // $data['admin'] = Session::get('admin');
  18. // 获取继承父类Base的属性
  19. $data['admin'] = $this->admin;
  20. // 用户角色(根据用户信息的角色gid查询对应的角色名称)
  21. // $data['group'] = Db::table('admin_group')->where('id',$data['admin']['gid'])->find();
  22. $data['group'] = $this->mygroup;
  23. /*** 方案一:直接读数据库 根据一级菜单循环读取二级菜单
  24. // 查询一级菜单(pid==0)
  25. $data['menuList'] = Db::table('admin_menu')->where([['pid','=','0'],['status','=','0']])->select()->toArray();
  26. // 根据一级菜单mid 查询对应的pid二级菜单
  27. foreach ($data['menuList'] as $key => $menu) {
  28. $data['menuList'][$key]['children'] = Db::table('admin_menu')->where('pid','=',$menu['mid'])->select();
  29. }
  30. **/
  31. /*** 方案二:优化数据读取 将一级和二级分别都读取出来,根据一级的循环遍历查找对应二级
  32. // 查询出所有一级菜单 (pid==0)
  33. $data['menuList'] = Db::table('admin_menu')->where([['pid','=','0'],['status','=','0']])->select()->toArray();
  34. // 查询出所有二级菜单 (pid > 0)
  35. $secondLists = Db::table('admin_menu')->where([['pid','>','0'],['status','=','0']])->select()->toArray();
  36. // 遍历一级菜单
  37. foreach ($data['menuList'] as $key => $menu) {
  38. // 遍历二级菜单
  39. $data['menuList'][$key]['children'] = [];
  40. foreach ($secondLists as $chd) {
  41. // 根据一级菜单mid找出所有自己的二级菜单pid放到自己children的数组下面 二级pid == 一级mid
  42. if($menu['mid']==$chd['pid']){
  43. $data['menuList'][$key]['children'][] = $chd;
  44. }
  45. }
  46. }
  47. **/
  48. /*** 方案一:优化 原生PHP操作文件 将第一次读取的数据存储到文件中
  49. $cache_file = 'menuData.txt';
  50. // 检查文件是否存在,不存在则创建
  51. if(!file_exists($cache_file)){
  52. touch($cache_file);
  53. }
  54. // 读取文件数据
  55. $menus = file_get_contents($cache_file);
  56. // 判断是否读取到数据
  57. if(empty($menus)){
  58. $menus = Db::table('admin_menu')->where([['pid','=','0'],['status','=','0']])->select()->toArray();
  59. // 根据一级菜单mid 查询对应的pid二级菜单
  60. foreach ($menus as $key => $menu) {
  61. $menus[$key]['children'] = Db::table('admin_menu')->where('pid','=',$menu['mid'])->select();
  62. }
  63. // 将读取的数据存储起来
  64. file_put_contents($cache_file,json_encode($menus));
  65. }else{
  66. // 如果读取到了数据,我们将其转化为数组
  67. $menus = json_decode($menus,true);
  68. }
  69. $data['menuList'] = $menus;
  70. **/
  71. /*** 方案二: 使用tp的cache缓存机制存储 */
  72. $menus = Cache::get('menus');
  73. $where= [
  74. ['status','=','0'], // 筛选没有被禁用的菜单
  75. ['ishidden','=','0'], // 筛选可用显示的菜单
  76. ['mid','in',$this->mygroup['rights']] // 过滤有权限的菜单才显示
  77. ];
  78. if(empty($menus)){
  79. $menus = Db::table('admin_menu')->where('pid','=','0')->where($where)->select()->toArray();
  80. foreach ($menus as $key => $menu) {
  81. $menus[$key]['children'] = Db::table('admin_menu')->where('pid','=',$menu['mid'])->where($where)->select()->toArray();
  82. }
  83. Cache::set('menus',$menus);
  84. }
  85. $data['menuList'] = $menus;
  86. // 注意:以上两种方式虽然我们将数据存到了缓存文件中,避免了数据重复读取,但是当缓存文件存在时,他永远不会再从数据库中读取数据,这时在修改导航的数据时,需要先清除缓存文件,不然他不会被更新到页面中,此时的缓存数据还是第一次读取的旧数据。
  87. View::assign([
  88. 'data'=>$data
  89. ]);
  90. return View::fetch('/home/index');
  91. }
  92. public function welcome(){
  93. return View::fetch('/home/welcome');
  94. }
  95. }
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议