Blogger Information
Blog 128
fans 9
comment 5
visits 241242
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
2.【商城后台管理系统】基于TP6开发后台管理界面渲染与无限级菜单查询
 一纸荒凉* Armani
Original
1924 people have browsed it

【商城后台管理系统】基于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. }
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post