超级管理员后台的界面结构说明
表象
先来看左侧这个导航条是怎么来的?
让我们点击后台菜单看看 (这里原先是菜单管理,为了更好的区分我设置成了后台菜单)
点击右侧列表中的系统 我们就可以看到下面的界面 为了更好的说明问题 我对属性管理的分组做了去除操作
大家可以通过查看这里的设置来配置新的菜单
超级管理员的后台操作就是如此形成的,当大家在新建后台时就可以不用做太大的修改即可轻松的搞定模板
下面是一个模板展示 OT的Public/base模板的结构 将会对部分内容给大家作一些说明
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>{$meta_title}|{:C('TIPINFO')}管理平台</title>
<link href="__ROOT__/Public/favicon.ico" type="image/x-icon" rel="shortcut icon">
<link rel="stylesheet" type="text/css" href="__CSS__/base.css" media="all">
<link rel="stylesheet" type="text/css" href="__CSS__/common.css" media="all">
<link rel="stylesheet" type="text/css" href="__CSS__/module.css">
<link rel="stylesheet" type="text/css" href="__CSS__/style.css" media="all">
<link rel="stylesheet" type="text/css" href="__CSS__/{$Think.config.COLOR_STYLE}.css" media="all">
<!--[if lt IE 9]>
<script type="text/javascript" src="__STATIC__/jquery-1.10.2.min.js"></script>
<![endif]--><!--[if gte IE 9]><!-->
<script type="text/javascript" src="__STATIC__/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="__JS__/jquery.mousewheel.js"></script>
<!--<![endif]-->
<block name="style"></block>
</head>
<body>
<!-- 头部 -->
<div class="header">
<!-- Logo -->
<span class="logo"></span>
<!-- /Logo -->
<!-- 主导航 -->
<ul class="main-nav">
<volist name="__MENU__.main" id="menu">
<li class="{$menu.class|default=''}"><a href="{$menu.url|U}">{$menu.title}</a></li>
</volist>
</ul>
<!-- /主导航 -->
<!-- 用户栏 -->
<div class="user-bar">
<a href="javascript:;" class="user-entrance"><i class="icon-user"></i></a>
<ul class="nav-list user-menu hidden">
<li class="manager">你好,<em title="{:session('user_auth.username')}">{:session('user_auth.username')}</em></li>
<li><a href="{:U('User/updatePassword')}">修改密码</a></li>
<li><a href="{:U('User/updateNickname')}">修改昵称</a></li>
<li><a href="{:U('Public/logout')}">退出</a></li>
</ul>
</div>
</div>
<!-- /头部 -->
<!-- 边栏 -->
<div class="sidebar">
<!-- 子导航 -->
<block name="sidebar">
<div id="subnav" class="subnav">
<notempty name="_extra_menu">
{// 动态扩展菜单 //}
{:extra_menu($_extra_menu,$__MENU__)}
</notempty>
<!-- 哈哈 这里开始就是左侧菜单啦。。。。。 -->
<volist name="__MENU__.child" id="sub_menu">
<!-- 子导航 -->
<notempty name="sub_menu">
<notempty name="key"><h3><i class="icon icon-unfold"></i>{$key}</h3></notempty>
<ul class="side-sub-menu">
<volist name="sub_menu" id="menu">
<li>
<a class="item" href="{$menu.url|U}">{$menu.title}</a>
</li>
</volist>
</ul>
</notempty>
<!-- /子导航 -->
</volist>
</div>
</block>
<!-- /子导航 -->
</div>
<!-- /边栏 -->
<!-- 内容区 -->
<div id="main-content">
<div id="top-alert" class="fixed alert alert-error" style="display: none;">
<button class="close fixed" style="margin-top: 4px;">×</button>
<div class="alert-content">这是内容</div>
</div>
<div id="main" class="main">
<block name="nav">
<!-- nav -->
<notempty name="_show_nav">
<div class="breadcrumb">
<span>您的位置:</span>
<assign name="i" value="1" />
<foreach name="_nav" item="v" key="k">
<if condition="$i eq count($_nav)">
<span>{$v}</span>
<else />
<span><a href="{$k}">{$v}</a>></span>
</if>
<assign name="i" value="$i+1" />
</foreach>
</div>
</notempty>
<!-- nav -->
</block>
<block name="body"> </block>
</div>
<div class="cont-ft">
<div class="copyright">
<div class="fl">感谢使用<a href="http://www.bluemark.cn" target="_blank">{:C('TIPINFO')}</a></div>
<div class="fr">V{$Think.const.BP_VERSION}</div>
</div>
</div>
</div>
<!-- /内容区 -->
<script type="text/javascript">
(function(){
var ThinkPHP = window.Think = {
"ROOT" : "__ROOT__", //当前网站地址
"APP" : "__APP__", //当前项目地址
"PUBLIC" : "__PUBLIC__", //项目公共目录地址
"DEEP" : "{:C('URL_PATHINFO_DEPR')}", //PATHINFO分割符
"MODEL" : ["{:C('URL_MODEL')}", "{:C('URL_CASE_INSENSITIVE')}", "{:C('URL_HTML_SUFFIX')}"],
"VAR" : ["{:C('VAR_MODULE')}", "{:C('VAR_CONTROLLER')}", "{:C('VAR_ACTION')}"]
}
})();
</script>
<script type="text/javascript" src="__STATIC__/think.js"></script>
<script type="text/javascript" src="__JS__/common.js"></script>
<script type="text/javascript">
+function(){
var $window = $(window), $subnav = $("#subnav"), url;
$window.resize(function(){
$("#main").css("min-height", $window.height() - 80);
}).resize();
/* 左边菜单高亮 */
url = window.location.pathname + window.location.search;
url = url.replace(/(\/(p)\/\d+)|(&p=\d+)|(\/(id)\/\d+)|(&id=\d+)|(\/(group)\/\d+)|(&group=\d+)/, "");
$subnav.find("a[href='" + url + "']").parent().addClass("current");
/* 左边菜单显示收起 */
$("#subnav").on("click", "h3", function(){
var $this = $(this);
$this.find(".icon").toggleClass("icon-fold");
$this.next().slideToggle("fast").siblings(".side-sub-menu:visible").
prev("h3").find("i").addClass("icon-fold").end().end().hide();
});
$("#subnav h3 a").click(function(e){e.stopPropagation()});
/* 头部管理员菜单 */
$(".user-bar").mouseenter(function(){
var userMenu = $(this).children(".user-menu ");
userMenu.removeClass("hidden");
clearTimeout(userMenu.data("timeout"));
}).mouseleave(function(){
var userMenu = $(this).children(".user-menu");
userMenu.data("timeout") && clearTimeout(userMenu.data("timeout"));
userMenu.data("timeout", setTimeout(function(){userMenu.addClass("hidden")}, 100));
});
/* 表单获取焦点变色 */
$("form").on("focus", "input", function(){
$(this).addClass('focus');
}).on("blur","input",function(){
$(this).removeClass('focus');
});
$("form").on("focus", "textarea", function(){
$(this).closest('label').addClass('focus');
}).on("blur","textarea",function(){
$(this).closest('label').removeClass('focus');
});
// 导航栏超出窗口高度后的模拟滚动条
var sHeight = $(".sidebar").height();
var subHeight = $(".subnav").height();
var diff = subHeight - sHeight; //250
var sub = $(".subnav");
if(diff > 0){
$(window).mousewheel(function(event, delta){
if(delta>0){
if(parseInt(sub.css('marginTop'))>-10){
sub.css('marginTop','0px');
}else{
sub.css('marginTop','+='+10);
}
}else{
if(parseInt(sub.css('marginTop'))<'-'+(diff-10)){
sub.css('marginTop','-'+(diff-10));
}else{
sub.css('marginTop','-='+10);
}
}
});
}
}();
</script>
<block name="script"></block>
</body>
</html>
左侧菜单的生成就是通过
<volist name="__MENU__.child" id="sub_menu">
<!-- 子导航 -->
<notempty name="sub_menu">
<notempty name="key"><h3><i class="icon icon-unfold"></i>{$key}</h3></notempty>
<ul class="side-sub-menu">
<volist name="sub_menu" id="menu">
<li>
<a class="item" href="{$menu.url|U}">{$menu.title}</a>
</li>
</volist>
</ul>
</notempty>
<!-- /子导航 -->
</volist>
通过这里可以看到官方在构建OneThink时候也是下了很大的功夫的。
原理跟踪
至于官方是如何生成tree 并获取到的可以通过对extra_menu
这个方法进行debug来查看
这个方法的位置是 /Application/Admin/Common/function.phg 第253行
朱亚杰也对这个方法做了简单说明,但最主要的还是那个tree的生成。那么$__MENU__
参数是在哪里设置的呢?
可以参见 /Application/Admin/Controller/AdminController.class.php 第65行
这里的get_menu
方法便是这个东东了
动态扩展(假的啊)
大家在看代码的时候会看到这样的东东
{// 动态扩展菜单 //}
{:extra_menu($_extra_menu,$__MENU__)}
咦,这个东东是做什么用的?
让我们再来看个别的图
哈哈 傻眼了吧,我刚开始的时候看到这里的也是傻了。这里是怎么弄的呢?
看程序源码吧
#Application\Admin\Controller\ArticleController.class.php 第154行
$this->getMenu();
#然后查看getMenu()内容
/**
* 显示左边菜单,进行权限控制
* @author huajie <banhuajie@163.com>
*/
protected function getMenu(){
//获取动态分类
$cate_auth = AuthGroupModel::getAuthCategories(UID); //获取当前用户所有的内容权限节点
$cate_auth = $cate_auth == null ? array() : $cate_auth;
$cate = M('Category')->where(array('status'=>1))->field('id,title,pid,allow_publish')->order('pid,sort')->select();
//没有权限的分类则不显示
if(!IS_ROOT){
foreach ($cate as $key=>$value){
if(!in_array($value['id'], $cate_auth)){
unset($cate[$key]);
}
}
}
$cate = list_to_tree($cate); //生成分类树
//获取分类id
$cate_id = I('param.cate_id');
$this->cate_id = $cate_id;
//是否展开分类
$hide_cate = false;
if(ACTION_NAME != 'recycle' && ACTION_NAME != 'draftbox' && ACTION_NAME != 'mydocument'){
$hide_cate = true;
}
//生成每个分类的url
foreach ($cate as $key=>&$value){
$value['url'] = 'Article/index?cate_id='.$value['id'];
if($cate_id == $value['id'] && $hide_cate){
$value['current'] = true;
}else{
$value['current'] = false;
}
if(!empty($value['_child'])){
$is_child = false;
foreach ($value['_child'] as $ka=>&$va){
$va['url'] = 'Article/index?cate_id='.$va['id'];
if(!empty($va['_child'])){
foreach ($va['_child'] as $k=>&$v){
$v['url'] = 'Article/index?cate_id='.$v['id'];
$v['pid'] = $va['id'];
$is_child = $v['id'] == $cate_id ? true : false;
}
}
//展开子分类的父分类
if($va['id'] == $cate_id || $is_child){
$is_child = false;
if($hide_cate){
$value['current'] = true;
$va['current'] = true;
}else{
$value['current'] = false;
$va['current'] = false;
}
}else{
$va['current'] = false;
}
}
}
}
$this->assign('nodes', $cate);
$this->assign('cate_id', $this->cate_id);
//获取面包屑信息
$nav = get_parent_category($cate_id);
$this->assign('rightNav', $nav);
//获取回收站权限
$this->assign('show_recycle', IS_ROOT || $this->checkRule('Admin/article/recycle'));
//获取草稿箱权限
$this->assign('show_draftbox', C('OPEN_DRAFTBOX'));
//获取审核列表权限
$this->assign('show_examine', IS_ROOT || $this->checkRule('Admin/article/examine'));
}
这就出来了吗 ? 纳尼!? 怎么没看到个人中心的字样呢?
还是再看看模板吧
<div id="subnav" class="subnav">
<notempty name="_extra_menu">
{// 动态扩展菜单 //}
{:extra_menu($_extra_menu,$__MENU__)}
</notempty>
<volist name="__MENU__.child" id="sub_menu">
<!-- 子导航 -->
<notempty name="sub_menu">
<notempty name="key"><h3><i class="icon icon-unfold"></i>{$key}</h3></notempty>
<ul class="side-sub-menu">
<volist name="sub_menu" id="menu">
<li>
<a class="item" href="{$menu.url|U}">{$menu.title}</a>
</li>
</volist>
</ul>
</notempty>
<!-- /子导航 -->
</volist>
<h3>
<i class="icon <notin name="Think.ACTION_NAME" value="mydocument,draftbox,examine">icon-fold</notin>"></i>
个人中心
</h3>
<ul class="side-sub-menu <notin name="Think.ACTION_NAME" value="mydocument,draftbox,examine">subnav-off</notin>">
<li <eq name="Think.ACTION_NAME" value="mydocument">class="current"</eq>><a class="item" href="{:U('article/mydocument')}">我的文档</a></li>
<eq name="show_draftbox" value="1">
<li <eq name="Think.ACTION_NAME" value="draftbox">class="current"</eq>><a class="item" href="{:U('article/draftbox')}">草稿箱</a></li>
</eq>
<eq name="show_examine" value="1">
<li <eq name="Think.ACTION_NAME" value="examine">class="examine"</eq>><a class="item" href="{:U('article/examine')}">待审核</a></li>
</eq>
</ul>
<volist name="nodes" id="sub_menu">
<!-- 子导航 -->
<notempty name="sub_menu">
<h3>
<i class="icon <neq name="sub_menu['current']" value="1">icon-fold</neq>"></i>
<gt name="sub_menu['allow_publish']" value="0"><a class="item" href="{$sub_menu.url|U}">{$sub_menu.title}</a><else/>{$sub_menu.title}</gt>
</h3>
<ul class="side-sub-menu <neq name='sub_menu["current"]' value="1">subnav-off</neq>">
<volist name="sub_menu['_child']" id="menu">
<li <if condition="$menu['id'] eq $cate_id or $menu['current'] eq 1">class="current"</if>>
<gt name="menu['allow_publish']" value="0"><a class="item" href="{$menu.url|U}">{$menu.title}</a><else/><a class="item" href="javascript:void(0);">{$menu.title}</a></gt>
<!-- 一级子菜单 -->
<present name="menu['_child']">
<ul class="subitem">
<foreach name="menu['_child']" item="three_menu">
<li>
<gt name="three_menu['allow_publish']" value="0"><a class="item" href="{$three_menu.url|U}">{$three_menu.title}</a><else/><a class="item" href="javascript:void(0);">{$three_menu.title}</a></gt>
<!-- 二级子菜单 -->
<present name="three_menu['_child']">
<ul class="subitem">
<foreach name="three_menu['_child']" item="four_menu">
<li>
<gt name="four_menu['allow_publish']" value="0"><a class="item" href="{:U('index','cate_id='.$four_menu['id'])}">{$four_menu.title}</a><else/><a class="item" href="javascript:void(0);">{$four_menu.title}</a></gt>
<!-- 三级子菜单 -->
<present name="four_menu['_child']">
<ul class="subitem">
<volist name="four_menu['_child']" id="five_menu">
<li>
<gt name="five_menu['allow_publish']" value="0"><a class="item" href="{:U('index','cate_id='.$five_menu['id'])}">{$five_menu.title}</a><else/><a class="item" href="javascript:void(0);">{$five_menu.title}</a></gt>
</li>
</volist>
</ul>
</present>
<!-- end 三级子菜单 -->
</li>
</foreach>
</ul>
</present>
<!-- end 二级子菜单 -->
</li>
</foreach>
</ul>
</present>
<!-- end 一级子菜单 -->
</li>
</volist>
</ul>
</notempty>
<!-- /子导航 -->
</volist>
<!-- 回收站 -->
<eq name="show_recycle" value="1">
<h3>
<em class="recycle"></em>
<a href="{:U('article/recycle')}">回收站</a>
</h3>
</eq>
</div>
<script>
$(function(){
$(".side-sub-menu li").hover(function(){
$(this).addClass("hover");
},function(){
$(this).removeClass("hover");
});
})
</script>
卧槽,,竟然不是真正的 动态扩展这里是手动拼出来的。
不过也得感激官方这种想法也能够给出实例。。。
那么真正的动态扩展在哪里?
真正的动态扩展
再来看图
咦 有戏,赶紧去看代码
#Application\Admin\Controller\AddonsController.class.php 就在开头不用写行号了
*/
class AddonsController extends AdminController {
public function _initialize(){
$this->assign('_extra_menu',array(
'已装插件后台'=> D('Addons')->getAdminList(),
));
parent::_initialize();
}
看到了已安装插件,看到了_extra_menu
一切ok。。。
本章完