浏览代码

新增二级菜单,可在config.php中开启或关闭
新增渲染状态时搜索匹配
优化渲染图片时点击打开
修复渲染日期时非整型字段的BUG
优化通用搜索功能
优化后台显示基础大小为13px

Karson 7 年之前
父节点
当前提交
6e408fb2d1

+ 14 - 23
application/admin/controller/Index.php

@@ -30,21 +30,20 @@ class Index extends Backend
     public function index()
     {
         //左侧菜单
-        $menulist = $this->auth->getSidebar([
+        list($menulist, $navlist) = $this->auth->getSidebar([
             'dashboard' => 'hot',
             'addon'     => ['new', 'red', 'badge'],
             'auth/rule' => __('Menu'),
             'general'   => ['new', 'purple'],
-                ], $this->view->site['fixedpage']);
+        ], $this->view->site['fixedpage']);
         $action = $this->request->request('action');
-        if ($this->request->isPost())
-        {
-            if ($action == 'refreshmenu')
-            {
-                $this->success('', null, ['menulist' => $menulist]);
+        if ($this->request->isPost()) {
+            if ($action == 'refreshmenu') {
+                $this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]);
             }
         }
         $this->view->assign('menulist', $menulist);
+        $this->view->assign('navlist', $navlist);
         $this->view->assign('title', __('Home'));
         return $this->view->fetch();
     }
@@ -55,12 +54,10 @@ class Index extends Backend
     public function login()
     {
         $url = $this->request->get('url', 'index/index');
-        if ($this->auth->isLogin())
-        {
+        if ($this->auth->isLogin()) {
             $this->success(__("You've logged in, do not login again"), $url);
         }
-        if ($this->request->isPost())
-        {
+        if ($this->request->isPost()) {
             $username = $this->request->post('username');
             $password = $this->request->post('password');
             $keeplogin = $this->request->post('keeplogin');
@@ -75,26 +72,21 @@ class Index extends Backend
                 'password'  => $password,
                 '__token__' => $token,
             ];
-            if (Config::get('fastadmin.login_captcha'))
-            {
+            if (Config::get('fastadmin.login_captcha')) {
                 $rule['captcha'] = 'require|captcha';
                 $data['captcha'] = $this->request->post('captcha');
             }
             $validate = new Validate($rule, [], ['username' => __('Username'), 'password' => __('Password'), 'captcha' => __('Captcha')]);
             $result = $validate->check($data);
-            if (!$result)
-            {
+            if (!$result) {
                 $this->error($validate->getError(), $url, ['token' => $this->request->token()]);
             }
             AdminLog::setTitle(__('Login'));
             $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0);
-            if ($result === true)
-            {
+            if ($result === true) {
                 Hook::listen("admin_login_after", $this->request);
                 $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]);
-            }
-            else
-            {
+            } else {
                 $msg = $this->auth->getError();
                 $msg = $msg ? $msg : __('Username or password is incorrect');
                 $this->error($msg, $url, ['token' => $this->request->token()]);
@@ -102,12 +94,11 @@ class Index extends Backend
         }
 
         // 根据客户端的cookie,判断是否可以自动登录
-        if ($this->auth->autologin())
-        {
+        if ($this->auth->autologin()) {
             $this->redirect($url);
         }
         $background = Config::get('fastadmin.login_background');
-        $background = stripos($background, 'http')===0 ? $background : config('site.cdnurl') . $background;
+        $background = stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background;
         $this->view->assign('background', $background);
         $this->view->assign('title', __('Login'));
         Hook::listen("admin_login_init", $this->request);

+ 80 - 92
application/admin/library/Auth.php

@@ -31,26 +31,23 @@ class Auth extends \fast\Auth
     /**
      * 管理员登录
      *
-     * @param   string  $username   用户名
-     * @param   string  $password   密码
-     * @param   int     $keeptime   有效时长
+     * @param   string $username 用户名
+     * @param   string $password 密码
+     * @param   int $keeptime 有效时长
      * @return  boolean
      */
     public function login($username, $password, $keeptime = 0)
     {
         $admin = Admin::get(['username' => $username]);
-        if (!$admin)
-        {
+        if (!$admin) {
             $this->setError('Username is incorrect');
             return false;
         }
-        if (Config::get('fastadmin.login_failure_retry') && $admin->loginfailure >= 10 && time() - $admin->updatetime < 86400)
-        {
+        if (Config::get('fastadmin.login_failure_retry') && $admin->loginfailure >= 10 && time() - $admin->updatetime < 86400) {
             $this->setError('Please try again after 1 day');
             return false;
         }
-        if ($admin->password != md5(md5($password) . $admin->salt))
-        {
+        if ($admin->password != md5(md5($password) . $admin->salt)) {
             $admin->loginfailure++;
             $admin->save();
             $this->setError('Password is incorrect');
@@ -71,8 +68,7 @@ class Auth extends \fast\Auth
     public function logout()
     {
         $admin = Admin::get(intval($this->id));
-        if (!$admin)
-        {
+        if (!$admin) {
             return true;
         }
         $admin->token = '';
@@ -89,30 +85,24 @@ class Auth extends \fast\Auth
     public function autologin()
     {
         $keeplogin = Cookie::get('keeplogin');
-        if (!$keeplogin)
-        {
+        if (!$keeplogin) {
             return false;
         }
         list($id, $keeptime, $expiretime, $key) = explode('|', $keeplogin);
-        if ($id && $keeptime && $expiretime && $key && $expiretime > time())
-        {
+        if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
             $admin = Admin::get($id);
-            if (!$admin || !$admin->token)
-            {
+            if (!$admin || !$admin->token) {
                 return false;
             }
             //token有变更
-            if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token))
-            {
+            if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token)) {
                 return false;
             }
             Session::set("admin", $admin->toArray());
             //刷新自动登录的时效
             $this->keeplogin($keeptime);
             return true;
-        }
-        else
-        {
+        } else {
             return false;
         }
     }
@@ -120,13 +110,12 @@ class Auth extends \fast\Auth
     /**
      * 刷新保持登录的Cookie
      *
-     * @param   int     $keeptime
+     * @param   int $keeptime
      * @return  boolean
      */
     protected function keeplogin($keeptime = 0)
     {
-        if ($keeptime)
-        {
+        if ($keeptime) {
             $expiretime = time() + $keeptime;
             $key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token);
             $data = [$this->id, $keeptime, $expiretime, $key];
@@ -150,15 +139,13 @@ class Auth extends \fast\Auth
     {
         $request = Request::instance();
         $arr = is_array($arr) ? $arr : explode(',', $arr);
-        if (!$arr)
-        {
+        if (!$arr) {
             return FALSE;
         }
 
         $arr = array_map('strtolower', $arr);
         // 是否存在
-        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr))
-        {
+        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
             return TRUE;
         }
 
@@ -173,21 +160,17 @@ class Auth extends \fast\Auth
      */
     public function isLogin()
     {
-        if ($this->logined)
-        {
+        if ($this->logined) {
             return true;
         }
         $admin = Session::get('admin');
-        if (!$admin)
-        {
+        if (!$admin) {
             return false;
         }
         //判断是否同一时间同一账号只能在一个地方登录
-        if (Config::get('fastadmin.login_unique'))
-        {
+        if (Config::get('fastadmin.login_unique')) {
             $my = Admin::get($admin['id']);
-            if (!$my || $my['token'] != $admin['token'])
-            {
+            if (!$my || $my['token'] != $admin['token']) {
                 return false;
             }
         }
@@ -252,9 +235,8 @@ class Auth extends \fast\Auth
     {
         $groups = $this->getGroups($uid);
         $groupIds = [];
-        foreach ($groups as $K => $v)
-        {
-            $groupIds[] = (int) $v['group_id'];
+        foreach ($groups as $K => $v) {
+            $groupIds[] = (int)$v['group_id'];
         }
         return $groupIds;
     }
@@ -269,17 +251,14 @@ class Auth extends \fast\Auth
         //取出当前管理员所有的分组
         $groups = $this->getGroups();
         $groupIds = [];
-        foreach ($groups as $k => $v)
-        {
+        foreach ($groups as $k => $v) {
             $groupIds[] = $v['id'];
         }
         // 取出所有分组
         $groupList = \app\admin\model\AuthGroup::where(['status' => 'normal'])->select();
         $objList = [];
-        foreach ($groups as $K => $v)
-        {
-            if ($v['rules'] === '*')
-            {
+        foreach ($groups as $K => $v) {
+            if ($v['rules'] === '*') {
                 $objList = $groupList;
                 break;
             }
@@ -289,12 +268,10 @@ class Auth extends \fast\Auth
             $objList = array_merge($objList, Tree::instance()->getTreeList($obj));
         }
         $childrenGroupIds = [];
-        foreach ($objList as $k => $v)
-        {
+        foreach ($objList as $k => $v) {
             $childrenGroupIds[] = $v['id'];
         }
-        if (!$withself)
-        {
+        if (!$withself) {
             $childrenGroupIds = array_diff($childrenGroupIds, $groupIds);
         }
         return $childrenGroupIds;
@@ -308,33 +285,25 @@ class Auth extends \fast\Auth
     public function getChildrenAdminIds($withself = false)
     {
         $childrenAdminIds = [];
-        if (!$this->isSuperAdmin())
-        {
+        if (!$this->isSuperAdmin()) {
             $groupIds = $this->getChildrenGroupIds(false);
             $authGroupList = \app\admin\model\AuthGroupAccess::
-                    field('uid,group_id')
-                    ->where('group_id', 'in', $groupIds)
-                    ->select();
+            field('uid,group_id')
+                ->where('group_id', 'in', $groupIds)
+                ->select();
 
-            foreach ($authGroupList as $k => $v)
-            {
+            foreach ($authGroupList as $k => $v) {
                 $childrenAdminIds[] = $v['uid'];
             }
-        }
-        else
-        {
+        } else {
             //超级管理员拥有所有人的权限
             $childrenAdminIds = Admin::column('id');
         }
-        if ($withself)
-        {
-            if (!in_array($this->id, $childrenAdminIds))
-            {
+        if ($withself) {
+            if (!in_array($this->id, $childrenAdminIds)) {
                 $childrenAdminIds[] = $this->id;
             }
-        }
-        else
-        {
+        } else {
             $childrenAdminIds = array_diff($childrenAdminIds, [$this->id]);
         }
         return $childrenAdminIds;
@@ -350,15 +319,12 @@ class Auth extends \fast\Auth
         if ($this->breadcrumb || !$path)
             return $this->breadcrumb;
         $path_rule_id = 0;
-        foreach ($this->rules as $rule)
-        {
+        foreach ($this->rules as $rule) {
             $path_rule_id = $rule['name'] == $path ? $rule['id'] : $path_rule_id;
         }
-        if ($path_rule_id)
-        {
+        if ($path_rule_id) {
             $this->breadcrumb = Tree::instance()->init($this->rules)->getParents($path_rule_id, true);
-            foreach ($this->breadcrumb as $k => &$v)
-            {
+            foreach ($this->breadcrumb as $k => &$v) {
                 $v['url'] = url($v['name']);
                 $v['title'] = __($v['title']);
             }
@@ -367,10 +333,11 @@ class Auth extends \fast\Auth
     }
 
     /**
-     * 获取左侧菜单栏
+     * 获取左侧和顶部菜单栏
      *
      * @param array $params URL对应的badge数据
-     * @return string
+     * @param string $fixedPage 默认页
+     * @return array
      */
     public function getSidebar($params = [], $fixedPage = 'dashboard')
     {
@@ -379,26 +346,21 @@ class Auth extends \fast\Auth
         $badgeList = [];
         $module = request()->module();
         // 生成菜单的badge
-        foreach ($params as $k => $v)
-        {
+        foreach ($params as $k => $v) {
 
             $url = $k;
 
-            if (is_array($v))
-            {
+            if (is_array($v)) {
                 $nums = isset($v[0]) ? $v[0] : 0;
                 $color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
                 $class = isset($v[2]) ? $v[2] : 'label';
-            }
-            else
-            {
+            } else {
                 $nums = $v;
                 $color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
                 $class = 'label';
             }
             //必须nums大于0才显示
-            if ($nums)
-            {
+            if ($nums) {
                 $badgeList[$url] = '<small class="' . $class . ' pull-right bg-' . $color . '">' . $nums . '</small>';
             }
         }
@@ -409,10 +371,8 @@ class Auth extends \fast\Auth
         $pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader');
         // 必须将结果集转换为数组
         $ruleList = collection(\app\admin\model\AuthRule::where('status', 'normal')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray();
-        foreach ($ruleList as $k => &$v)
-        {
-            if (!in_array($v['name'], $userRule))
-            {
+        foreach ($ruleList as $k => &$v) {
+            if (!in_array($v['name'], $userRule)) {
                 unset($ruleList[$k]);
                 continue;
             }
@@ -423,16 +383,44 @@ class Auth extends \fast\Auth
             $v['pinyin'] = $pinyin->permalink($v['title'], '');
             $v['title'] = __($v['title']);
         }
-        // 构造菜单数据
-        Tree::instance()->init($ruleList);
-        $menu = Tree::instance()->getTreeMenu(0, '<li class="@class"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
-        return $menu;
+
+        $menu = $nav = '';
+        if (Config::get('fastadmin.multiplenav')) {
+            $topList = [];
+            foreach ($ruleList as $index => $item) {
+                if (!$item['pid']) {
+                    $topList[] = $item;
+                }
+            }
+            $selectParentIds = [];
+            $tree = Tree::instance();
+            $tree->init($ruleList);
+            if ($select_id) {
+                $selectParentIds = $tree->getParentsIds($select_id, true);
+            }
+            foreach ($topList as $index => $item) {
+                $childList = Tree::instance()->getTreeMenu($item['id'], '<li class="@class" pid="@pid"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
+                $current = in_array($item['id'], $selectParentIds);
+                $url = $childList ? 'javascript:;' : $item['url'];
+                $childList = str_replace('" pid="' . $item['id'] . '"', ' treeview ' . ($current ? '' : 'hidden') . '" pid="' . $item['id'] . '"', $childList);
+                $nav .= '<li class="' . ($current ? 'active' : '') . '"><a href="' . $url . '" addtabs="' . $item['id'] . '" url="' . $url . '"><i class="' . $item['icon'] . '"></i> <span>' . $item['title'] . '</span> <span class="pull-right-container"> </span></a> </li>';
+                $menu .= $childList;
+            }
+        } else {
+            // 构造菜单数据
+            Tree::instance()->init($ruleList);
+            $menu = Tree::instance()->getTreeMenu(0, '<li class="@class"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
+        }
+
+
+        return [$menu, $nav];
     }
 
     /**
      * 设置错误信息
      *
      * @param $error 错误信息
+     * @return Auth
      */
     public function setError($error)
     {

+ 120 - 104
application/admin/view/common/header.html

@@ -1,131 +1,147 @@
 <!-- Logo -->
-<a href="javascript:;" class="logo hidden-xs">
+<a href="javascript:;" class="logo">
     <!-- 迷你模式下Logo的大小为50X50 -->
     <span class="logo-mini">{$site.name|mb_substr=0,4,'utf-8'|mb_strtoupper='utf-8'}</span>
     <!-- 普通模式下Logo -->
     <span class="logo-lg"><b>{$site.name|mb_substr=0,4,'utf-8'}</b>{$site.name|mb_substr=4,null,'utf-8'}</span>
 </a>
+
 <!-- 顶部通栏样式 -->
 <nav class="navbar navbar-static-top">
-    <!-- 边栏切换按钮-->
-    <a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
-        <span class="sr-only">{:__('Toggle navigation')}</span>
-    </a>
 
-    <div id="nav" class="pull-left">
+    <!--第一级菜单-->
+    <div id="firstnav">
+        <!-- 边栏切换按钮-->
+        <a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
+            <span class="sr-only">{:__('Toggle navigation')}</span>
+        </a>
+
         <!--如果不想在顶部显示角标,则给ul加上disable-top-badge类即可-->
-        <ul class="nav nav-tabs nav-addtabs disable-top-badge" role="tablist">
+        <ul class="nav nav-tabs nav-addtabs disable-top-badge hidden-xs" role="tablist">
+            {$navlist}
         </ul>
-    </div>
 
-    <div class="navbar-custom-menu">
-        <ul class="nav navbar-nav">
+        <div class="navbar-custom-menu">
+            <ul class="nav navbar-nav">
 
-            <li>
-                <a href="__PUBLIC__" target="_blank"><i class="fa fa-home" style="font-size:14px;"></i></a>
-            </li>
+                <li>
+                    <a href="__PUBLIC__" target="_blank"><i class="fa fa-home" style="font-size:14px;"></i></a>
+                </li>
 
-            <li class="dropdown notifications-menu hidden-xs">
-                <a href="#" class="dropdown-toggle" data-toggle="dropdown">
-                    <i class="fa fa-bell-o"></i>
-                    <span class="label label-warning"></span>
-                </a>
-                <ul class="dropdown-menu">
-                    <li class="header">{:__('Latest news')}</li>
-                    <li>
-                        <!-- FastAdmin最新更新信息,你可以替换成你自己站点的信息,请注意修改public/assets/js/backend/index.js文件 -->
-                        <ul class="menu">
+                <li class="dropdown notifications-menu hidden-xs">
+                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+                        <i class="fa fa-bell-o"></i>
+                        <span class="label label-warning"></span>
+                    </a>
+                    <ul class="dropdown-menu">
+                        <li class="header">{:__('Latest news')}</li>
+                        <li>
+                            <!-- FastAdmin最新更新信息,你可以替换成你自己站点的信息,请注意修改public/assets/js/backend/index.js文件 -->
+                            <ul class="menu">
 
-                        </ul>
-                    </li>
-                    <li class="footer"><a href="#" target="_blank">{:__('View more')}</a></li>
-                </ul>
-            </li>
+                            </ul>
+                        </li>
+                        <li class="footer"><a href="#" target="_blank">{:__('View more')}</a></li>
+                    </ul>
+                </li>
 
-            <li class="hidden-xs">
-                <a href="javascript:;" data-toggle="checkupdate" title="{:__('Check for updates')}">
-                    <i class="fa fa-refresh"></i>
-                </a>
-            </li>
+                <!-- 账号信息下拉框 -->
+                <li class="hidden-xs">
+                    <a href="javascript:;" data-toggle="checkupdate" title="{:__('Check for updates')}">
+                        <i class="fa fa-refresh"></i>
+                    </a>
+                </li>
 
-            <li>
-                <a href="javascript:;" data-toggle="dropdown" title="{:__('Wipe cache')}">
-                    <i class="fa fa-trash"></i>
-                </a>
-                <ul class="dropdown-menu wipecache">
-                    <li><a href="javascript:;" data-type="all"><i class="fa fa-trash"></i> {:__('Wipe all cache')}</a></li>
-                    <li class="divider"></li>
-                    <li><a href="javascript:;" data-type="content"><i class="fa fa-file-text"></i> {:__('Wipe content cache')}</a></li>
-                    <li><a href="javascript:;" data-type="template"><i class="fa fa-file-image-o"></i> {:__('Wipe template cache')}</a></li>
-                    <li><a href="javascript:;" data-type="addons"><i class="fa fa-rocket"></i> {:__('Wipe addons cache')}</a></li>
-                </ul>
-            </li>
+                <!-- 清除缓存 -->
+                <li>
+                    <a href="javascript:;" data-toggle="dropdown" title="{:__('Wipe cache')}">
+                        <i class="fa fa-trash"></i>
+                    </a>
+                    <ul class="dropdown-menu wipecache">
+                        <li><a href="javascript:;" data-type="all"><i class="fa fa-trash"></i> {:__('Wipe all cache')}</a></li>
+                        <li class="divider"></li>
+                        <li><a href="javascript:;" data-type="content"><i class="fa fa-file-text"></i> {:__('Wipe content cache')}</a></li>
+                        <li><a href="javascript:;" data-type="template"><i class="fa fa-file-image-o"></i> {:__('Wipe template cache')}</a></li>
+                        <li><a href="javascript:;" data-type="addons"><i class="fa fa-rocket"></i> {:__('Wipe addons cache')}</a></li>
+                    </ul>
+                </li>
 
-            {if $Think.config.lang_switch_on}
-            <li class="hidden-xs">
-                <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-language"></i></a>
-                <ul class="dropdown-menu">
-                    <li class="{$config['language']=='zh-cn'?'active':''}">
-                        <a href="?ref=addtabs&lang=zh-cn">简体中文</a>
-                    </li>
-                    <li class="{$config['language']=='en'?'active':''}">
-                        <a href="?ref=addtabs&lang=en">English</a>
-                    </li>
-                </ul>
-            </li>
-            {/if}
+                <!-- 多语言列表 -->
+                {if $Think.config.lang_switch_on}
+                <li class="hidden-xs">
+                    <a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-language"></i></a>
+                    <ul class="dropdown-menu">
+                        <li class="{$config['language']=='zh-cn'?'active':''}">
+                            <a href="?ref=addtabs&lang=zh-cn">简体中文</a>
+                        </li>
+                        <li class="{$config['language']=='en'?'active':''}">
+                            <a href="?ref=addtabs&lang=en">English</a>
+                        </li>
+                    </ul>
+                </li>
+                {/if}
 
-            <li class="hidden-xs">
-                <a href="#" data-toggle="fullscreen"><i class="fa fa-arrows-alt"></i></a>
-            </li>
+                <!-- 全屏按钮 -->
+                <li class="hidden-xs">
+                    <a href="#" data-toggle="fullscreen"><i class="fa fa-arrows-alt"></i></a>
+                </li>
 
-            <!-- 账号信息下拉框 -->
-            <li class="dropdown user user-menu">
-                <a href="#" class="dropdown-toggle" data-toggle="dropdown">
-                    <img src="__CDN__{$admin.avatar}" class="user-image" alt="{$admin.nickname}">
-                    <span class="hidden-xs">{$admin.nickname}</span>
-                </a>
-                <ul class="dropdown-menu">
-                    <!-- User image -->
-                    <li class="user-header">
-                        <img src="__CDN__{$admin.avatar}" class="img-circle" alt="">
+                <!-- 账号信息下拉框 -->
+                <li class="dropdown user user-menu">
+                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+                        <img src="__CDN__{$admin.avatar}" class="user-image" alt="{$admin.nickname}">
+                        <span class="hidden-xs">{$admin.nickname}</span>
+                    </a>
+                    <ul class="dropdown-menu">
+                        <!-- User image -->
+                        <li class="user-header">
+                            <img src="__CDN__{$admin.avatar}" class="img-circle" alt="">
 
-                        <p>
-                            {$admin.nickname}
-                            <small>{$admin.logintime|date="Y-m-d H:i:s",###}</small>
-                        </p>
-                    </li>
-                    <!-- Menu Body -->
-                    <li class="user-body">
-                        <div class="row">
-                            <div class="col-xs-4 text-center">
-                                <a href="https://www.fastadmin.net" target="_blank">{:__('FastAdmin')}</a>
+                            <p>
+                                {$admin.nickname}
+                                <small>{$admin.logintime|date="Y-m-d H:i:s",###}</small>
+                            </p>
+                        </li>
+                        <!-- Menu Body -->
+                        <li class="user-body">
+                            <div class="row">
+                                <div class="col-xs-4 text-center">
+                                    <a href="https://www.fastadmin.net" target="_blank">{:__('FastAdmin')}</a>
+                                </div>
+                                <div class="col-xs-4 text-center">
+                                    <a href="https://forum.fastadmin.net" target="_blank">{:__('Forum')}</a>
+                                </div>
+                                <div class="col-xs-4 text-center">
+                                    <a href="https://doc.fastadmin.net" target="_blank">{:__('Docs')}</a>
+                                </div>
                             </div>
-                            <div class="col-xs-4 text-center">
-                                <a href="https://forum.fastadmin.net" target="_blank">{:__('Forum')}</a>
+                        </li>
+                        <!-- Menu Footer-->
+                        <li class="user-footer">
+                            <div class="pull-left">
+                                <a href="general/profile" class="btn btn-primary addtabsit"><i class="fa fa-user"></i>
+                                    {:__('Profile')}</a>
                             </div>
-                            <div class="col-xs-4 text-center">
-                                <a href="https://doc.fastadmin.net" target="_blank">{:__('Docs')}</a>
+                            <div class="pull-right">
+                                <a href="{:url('index/logout')}" class="btn btn-danger"><i class="fa fa-sign-out"></i>
+                                    {:__('Logout')}</a>
                             </div>
-                        </div>
-                    </li>
-                    <!-- Menu Footer-->
-                    <li class="user-footer">
-                        <div class="pull-left">
-                            <a href="general/profile" class="btn btn-primary addtabsit"><i class="fa fa-user"></i>
-                                {:__('Profile')}</a>
-                        </div>
-                        <div class="pull-right">
-                            <a href="{:url('index/logout')}" class="btn btn-danger"><i class="fa fa-sign-out"></i>
-                                {:__('Logout')}</a>
-                        </div>
-                    </li>
-                </ul>
-            </li>
-            <!-- 控制栏切换按钮 -->
-            <li class="hidden-xs">
-                <a href="javascript:;" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a>
-            </li>
+                        </li>
+                    </ul>
+                </li>
+                <!-- 控制栏切换按钮 -->
+                <li class="hidden-xs">
+                    <a href="javascript:;" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a>
+                </li>
+            </ul>
+        </div>
+    </div>
+
+    {if $config.fastadmin.multiplenav}
+    <!--第二级菜单,只有在multiplenav开启时才显示-->
+    <div id="secondnav">
+        <ul class="nav nav-tabs nav-addtabs disable-top-badge" role="tablist">
         </ul>
     </div>
+    {/if}
 </nav>

+ 15 - 9
application/admin/view/common/menu.html

@@ -1,6 +1,6 @@
-<!-- sidebar: style can be found in sidebar.less -->
+<!-- 左侧菜单栏 -->
 <section class="sidebar">
-    <!-- Sidebar user panel -->
+    <!-- 管理员信息 -->
     <div class="user-panel hidden-xs">
         <div class="pull-left image">
             <a href="general/profile" class="addtabsit"><img src="__CDN__{$admin.avatar}" class="img-circle" /></a>
@@ -11,7 +11,7 @@
         </div>
     </div>
 
-    <!-- search form -->
+    <!-- 菜单搜索 -->
     <form action="" method="get" class="sidebar-form" onsubmit="return false;">
         <div class="input-group">
             <input type="text" name="q" class="form-control" placeholder="{:__('Search menu')}">
@@ -23,16 +23,22 @@
             </div>
         </div>
     </form>
-    <!-- /.search form -->
 
-    <!-- sidebar menu: : style can be found in sidebar.less -->
-    <!--如果想始终显示子菜单,则给ul加上show-submenu类即可-->
-    <ul class="sidebar-menu">
+    <!-- 移动端一级菜单 -->
+    <div class="mobilenav visible-xs">
+
+    </div>
+
+    <!--如果想始终显示子菜单,则给ul加上show-submenu类即可,当multiplenav开启的情况下默认为展开-->
+    <ul class="sidebar-menu {if $config.fastadmin.multiplenav}show-submenu{/if}">
+
+        <!-- 菜单可以在 后台管理->权限管理->菜单规则 中进行增删改排序 -->
         {$menulist}
+
+        <!--以下4行可以删除或改成自己的链接,但建议你在你的网站上添加一个FastAdmin的链接-->
         <li class="header" data-rel="external">{:__('Links')}</li>
         <li data-rel="external"><a href="https://doc.fastadmin.net" target="_blank"><i class="fa fa-list text-red"></i> <span>{:__('Docs')}</span></a></li>
         <li data-rel="external"><a href="https://forum.fastadmin.net" target="_blank"><i class="fa fa-comment text-yellow"></i> <span>{:__('Forum')}</span></a></li>
         <li data-rel="external"><a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank"><i class="fa fa-qq text-aqua"></i> <span>{:__('QQ qun')}</span></a></li>
     </ul>
-</section>
-<!-- /.sidebar -->
+</section>

+ 12 - 10
application/admin/view/index/index.html

@@ -1,38 +1,40 @@
 <!DOCTYPE html>
 <html lang="{$config.language}">
     <head>
+        <!-- 加载部部样式及META信息 -->
         {include file="common/meta" /}
     </head>
-    <body class="hold-transition skin-green sidebar-mini fixed" id="tabs">
+    <body class="hold-transition skin-green sidebar-mini fixed {if $config.fastadmin.multiplenav}multiplenav{/if}" id="tabs">
         <div class="wrapper">
 
+            <!-- 头部区域 -->
             <header id="header" class="main-header">
                 {include file='common/header' /}
             </header>
-            <!-- Left side column. contains the logo and sidebar -->
+
+            <!-- 左侧菜单栏 -->
             <aside class="main-sidebar">
                 {include file='common/menu' /}
             </aside>
 
-            <!-- Content Wrapper. Contains page content -->
+            <!-- 主体内容区域 -->
             <div class="content-wrapper tab-content tab-addtabs">
 
             </div>
-            <!-- /.content-wrapper -->
+
+            <!-- 底部链接,默认隐藏 -->
             <footer class="main-footer hide">
                 <div class="pull-right hidden-xs">
                 </div>
-                <strong>Copyright &copy; 2017-2018 <a href="https://www.fastadmin.net">Fastadmin</a>.</strong> All rights
-                reserved.
+                <strong>Copyright &copy; 2017-2018 <a href="https://www.fastadmin.net">Fastadmin</a>.</strong> All rights reserved.
             </footer>
 
-            <!-- Add the sidebar's background. This div must be placed
-                 immediately after the control sidebar -->
+            <!-- 右侧控制栏 -->
             <div class="control-sidebar-bg"></div>
             {include file="common/control" /}
         </div>
-        <!-- ./wrapper -->
-        <!-- end main content -->
+
+        <!-- 加载JS脚本 -->
         {include file="common/script" /}
     </body>
 </html>

+ 1 - 1
application/admin/view/layout/default.html

@@ -16,7 +16,7 @@
                                     <small>{:__('Control panel')}</small>
                                 </h1>
                             </section>
-                            {if !IS_DIALOG}
+                            {if !IS_DIALOG && !$config.fastadmin.multiplenav}
                             <!-- RIBBON -->
                             <div id="ribbon">
                                 <ol class="breadcrumb pull-left">

+ 2 - 0
application/config.php

@@ -267,6 +267,8 @@ return [
         'login_unique'        => false,
         //登录页默认背景图
         'login_background'    => "/assets/img/loginbg.jpg",
+        //是否启用多级菜单导航
+        'multiplenav'         => false,
         //自动检测更新
         'checkupdate'         => false,
         //版本号

+ 0 - 3
bower.json

@@ -33,8 +33,5 @@
     "fastadmin-dragsort": "~1.0.0",
     "fastadmin-addtabs": "~1.0.0",
     "fastadmin-selectpage": "~1.0.0"
-  },
-  "resolutions": {
-    "jspdf": "1.1.239 || 1.3.2"
   }
 }

+ 215 - 50
public/assets/css/backend.css

@@ -9,9 +9,11 @@
 @import url("../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css");
 @import url("../libs/bootstrap-daterangepicker/daterangepicker.css");
 @import url("../libs/nice-validator/dist/jquery.validator.css");
+@import url("../libs/bootstrap-select/dist/css/bootstrap-select.min.css");
 @import url("../libs/fastadmin-selectpage/selectpage.css");
 body {
   background: #f1f4f6;
+  font-size: 13px;
 }
 body.is-dialog {
   background: #fff;
@@ -31,19 +33,6 @@ body.is-dialog {
 .main-header .navbar {
   position: relative;
 }
-.main-header .navbar .sidebar-toggle {
-  position: absolute;
-  width: 45px;
-  text-align: center;
-}
-.main-header .navbar #nav {
-  position: absolute;
-  left: 45px;
-}
-.main-header .navbar .navbar-custom-menu {
-  position: absolute;
-  right: 0;
-}
 .bootstrap-dialog .modal-dialog {
   /*width: 70%;*/
   max-width: 885px;
@@ -60,7 +49,6 @@ html.ios-fix body {
 }
 #header {
   background: #fff;
-  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);
 }
 .content-wrapper {
   position: relative;
@@ -111,9 +99,6 @@ html.ios-fix body {
   margin-bottom: 15px;
   background-color: #f5f5f5;
 }
-.searchit {
-  border-bottom: 1px dashed #3c8dbc;
-}
 /* 固定的底部按钮 */
 .fixed-footer {
   position: fixed;
@@ -283,6 +268,7 @@ form.form-horizontal .control-label {
 }
 /*顶栏addtabs*/
 .nav-addtabs {
+  height: 100%;
   border: none;
 }
 .nav-addtabs.disable-top-badge > li > a > .pull-right-container {
@@ -292,8 +278,8 @@ form.form-horizontal .control-label {
   margin: 0;
 }
 .nav-addtabs > li > a {
-  height: 49px;
-  line-height: 49px;
+  height: 50px;
+  line-height: 50px;
   padding: 0 15px;
   border-radius: 0;
   border: none;
@@ -311,8 +297,8 @@ form.form-horizontal .control-label {
   margin-right: 3px;
 }
 .nav-addtabs > li.active > a {
-  height: 49px;
-  line-height: 49px;
+  height: 50px;
+  line-height: 50px;
   padding: 0 15px;
   border-radius: 0;
   border: none;
@@ -325,21 +311,28 @@ form.form-horizontal .control-label {
 .nav-addtabs > li.active > a:focus {
   border: none;
   color: #2c3e50;
-  border-right: 1px solid rgba(0, 0, 0, 0.05);
   background: #f1f4f6;
+  border-right: 1px solid rgba(0, 0, 0, 0.05);
 }
 .nav-addtabs > li .close-tab {
   font-size: 10px;
   position: absolute;
-  right: 5px;
+  right: 0px;
   top: 50%;
-  margin-top: -7px;
+  margin-top: -8px;
   z-index: 100;
-  cursor: hand;
   cursor: pointer;
-  color: #fff;
+  color: #eee;
   display: none;
 }
+.nav-addtabs > li .close-tab:before {
+  content: "\e626";
+  font-family: iconfont;
+  font-style: normal;
+  font-weight: normal;
+  text-decoration: inherit;
+  font-size: 18px;
+}
 .nav-addtabs .open > a:hover,
 .nav-addtabs .open > a:focus {
   border-right: 1px solid rgba(0, 0, 0, 0.05);
@@ -350,6 +343,119 @@ form.form-horizontal .control-label {
 .nav-addtabs li:hover > .close-tab {
   display: block;
 }
+.multiplenav .content-wrapper,
+.multiplenav .right-side {
+  padding-top: 94px;
+}
+.multiplenav #firstnav .nav-addtabs {
+  padding-right: 450px;
+}
+#firstnav {
+  height: 50px;
+  border-bottom: 1px solid transparent;
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+  position: relative;
+}
+#firstnav .sidebar-toggle {
+  position: absolute;
+  width: 45px;
+  text-align: center;
+  height: 50px;
+  line-height: 50px;
+  padding: 0;
+}
+#firstnav .nav-addtabs {
+  position: absolute;
+  left: 45px;
+  z-index: 98;
+}
+#firstnav .navbar-custom-menu {
+  position: absolute;
+  top: 0;
+  right: 0;
+  z-index: 99;
+  background: transparent;
+}
+/*次栏菜单栏*/
+#secondnav {
+  height: 44px;
+  position: absolute;
+  top: 50px;
+  left: 0;
+  background: #fff;
+  width: 100%;
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+  padding: 5px 10px;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
+}
+#secondnav .nav-addtabs {
+  height: 100%;
+  border: none;
+}
+#secondnav .nav-addtabs.disable-top-badge > li > a > .pull-right-container {
+  display: none;
+}
+#secondnav .nav-addtabs > li {
+  border: 1px solid #eee;
+  border-radius: 3px;
+  padding: 0 15px;
+  height: 30px;
+  line-height: 30px;
+  margin: 2px 5px 2px 0;
+  background: #fff;
+}
+#secondnav .nav-addtabs > li > a {
+  display: block;
+  color: #495060 !important;
+  height: 100%;
+  padding: 0;
+  line-height: 28px;
+  font-size: 12px;
+  vertical-align: middle;
+  opacity: 1;
+  overflow: hidden;
+  background: none;
+  border: none;
+}
+#secondnav .nav-addtabs > li > a i {
+  margin-right: 3px;
+}
+#secondnav .nav-addtabs > li.active {
+  border-color: #bdbebd;
+  background-color: #f7f7f7;
+}
+#secondnav .nav-addtabs > li .close-tab {
+  font-size: 10px;
+  position: absolute;
+  right: 0px;
+  top: 50%;
+  margin-top: -8px;
+  z-index: 100;
+  cursor: pointer;
+  color: #eee;
+}
+#secondnav .nav-addtabs > li .close-tab:before {
+  content: "\e626";
+  font-family: iconfont;
+  font-style: normal;
+  font-weight: normal;
+  text-decoration: inherit;
+  font-size: 18px;
+}
+#secondnav .nav-addtabs > li:hover,
+#secondnav .nav-addtabs > li:focus {
+  border-color: #bdbebd;
+}
+#secondnav .nav-addtabs ul li {
+  position: relative;
+}
+#secondnav .nav-addtabs li:hover > .close-tab {
+  display: block;
+  border-color: #222e32;
+  color: #222e32;
+}
 .main-sidebar .sidebar-form {
   overflow: visible;
 }
@@ -396,7 +502,7 @@ form.form-horizontal .control-label {
     top: 41px;
   }
   .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > .pull-right-container {
-    top: 7px!important;
+    top: 7px !important;
     height: 17px;
   }
 }
@@ -487,7 +593,7 @@ form.form-horizontal .control-label {
 }
 /*去除bootstrap-table的边框*/
 .fixed-table-container {
-  border: none!important;
+  border: none !important;
 }
 /*修复nice-validator新版下的一处BUG*/
 .nice-validator input,
@@ -535,7 +641,7 @@ form.form-horizontal .control-label {
 }
 .dropdown-menu.text-left a,
 .dropdown-menu.text-left li {
-  text-align: left!important;
+  text-align: left !important;
 }
 .bootstrap-table .fixed-table-toolbar .dropdown-menu {
   overflow: auto;
@@ -612,25 +718,25 @@ form.form-horizontal .control-label {
 }
 /*重写toast的几个背景色*/
 .toast-primary {
-  background-color: #48c9b0!important;
+  background-color: #48c9b0 !important;
 }
 .toast-success {
-  background-color: #18bc9c!important;
+  background-color: #18bc9c !important;
 }
 .toast-error {
-  background-color: #e74c3c!important;
+  background-color: #e74c3c !important;
 }
 .toast-info {
-  background-color: #5dade2!important;
+  background-color: #5dade2 !important;
 }
 .toast-warning {
-  background-color: #f1c40f!important;
+  background-color: #f1c40f !important;
 }
 .toast-inverse {
-  background-color: #34495e!important;
+  background-color: #34495e !important;
 }
 .toast-default {
-  background-color: #bdc3c7!important;
+  background-color: #bdc3c7 !important;
 }
 #toast-container > div,
 #toast-container > div:hover {
@@ -642,8 +748,8 @@ form.form-horizontal .control-label {
   /*自定义底部灰色操作区*/
 }
 .layui-layer-fast .layui-layer-title {
-  background: #2c3e50!important;
-  color: #fff!important;
+  background: #2c3e50 !important;
+  color: #fff !important;
   border-bottom: none;
 }
 .layui-layer-fast .layui-layer-title ~ .layui-layer-setwin {
@@ -656,7 +762,7 @@ form.form-horizontal .control-label {
   display: inline-block;
 }
 .layui-layer-fast.layui-layer-border {
-  border: none!important;
+  border: none !important;
   box-shadow: 1px 1px 50px rgba(0, 0, 0, 0.3) !important;
 }
 .layui-layer-fast.layui-layer-iframe {
@@ -668,15 +774,15 @@ form.form-horizontal .control-label {
   box-sizing: content-box;
 }
 .layui-layer-fast .layui-layer-btn {
-  text-align: center!important;
-  padding: 10px!important;
+  text-align: center !important;
+  padding: 10px !important;
   background: #ecf0f1;
   overflow: hidden;
 }
 .layui-layer-fast .layui-layer-btn a {
   background-color: #95a5a6;
   border-color: #95a5a6;
-  color: #fff!important;
+  color: #fff !important;
   height: 31px;
   margin-top: 0;
   border: 1px solid transparent;
@@ -689,10 +795,10 @@ form.form-horizontal .control-label {
   padding: 8px 20px;
   background-color: #ecf0f1;
   height: auto;
-  text-align: inherit!important;
+  text-align: inherit !important;
 }
 .layui-layer-fast .layui-layer-setwin > a {
-  background: none!important;
+  background: none !important;
 }
 .layui-layer-fast .layui-layer-setwin > a cite {
   display: none;
@@ -710,11 +816,11 @@ form.form-horizontal .control-label {
   z-index: 1;
 }
 .layui-layer-fast .layui-layer-setwin > a:hover {
-  text-decoration: none!important;
-  background: none!important;
+  text-decoration: none !important;
+  background: none !important;
 }
 .layui-layer-fast .layui-layer-setwin > a:focus {
-  text-decoration: none!important;
+  text-decoration: none !important;
 }
 .layui-layer-fast .layui-layer-setwin .layui-layer-min {
   display: none;
@@ -773,8 +879,12 @@ form.form-horizontal .control-label {
 }
 /*手机版样式*/
 @media (max-width: 480px) {
-  .nav-addtabs {
-    display: none;
+  #firstnav .navbar-custom-menu ul li a {
+    padding-left: 10px;
+    padding-right: 10px;
+  }
+  #firstnav .navbar-nav > .user-menu .user-image {
+    margin-top: -3px;
   }
   .fixed-table-toolbar .columns-right.btn-group {
     display: none;
@@ -783,9 +893,64 @@ form.form-horizontal .control-label {
   .fixed .right-side {
     padding-top: 50px;
   }
+  .multiplenav .fixed .content-wrapper,
+  .multiplenav .fixed .right-side {
+    padding-top: 94px;
+  }
+  .multiplenav .content-wrapper,
+  .multiplenav .right-side {
+    padding-top: 94px;
+  }
+  .main-sidebar,
+  .left-side {
+    padding-top: 144px;
+  }
 }
 /*平板样式*/
 @media (max-width: 768px) {
+  body .wrapper .main-header .logo {
+    background: none;
+    color: #fff;
+    border-bottom: 0 solid transparent;
+    position: absolute;
+    top: 0;
+    z-index: 1200;
+    width: 130px;
+    left: 50%;
+    margin-left: -65px;
+  }
+  body .sidebar .mobilenav a.btn-app {
+    color: #444;
+    width: 100px;
+    height: 70px;
+    font-size: 13px;
+  }
+  body .sidebar .mobilenav a.btn-app i.fa {
+    font-size: 24px;
+  }
+  body .sidebar .mobilenav a.btn-app span {
+    margin-top: 5px;
+    display: block;
+  }
+  body .sidebar .mobilenav a.btn-app.active {
+    color: #222d32;
+  }
+  body .wrapper .main-header .navbar .dropdown-menu li > a {
+    color: #333;
+  }
+  body .wrapper .main-header .navbar .dropdown-menu li > a:hover {
+    background: #eee;
+  }
+  body .wrapper .main-header .navbar .dropdown-menu li.active > a {
+    color: #fff;
+  }
+  body .wrapper .main-header .navbar .dropdown-menu li.active > a:hover {
+    background: #222d32;
+  }
+  .main-sidebar,
+  .left-side {
+    padding-top: 94px;
+  }
   .n-bootstrap .n-right {
     margin-top: 0;
     top: -20px;
@@ -811,5 +976,5 @@ form.form-horizontal .control-label {
   margin: 2px 0 0;
 }
 .wipecache li a {
-  color: #444444!important;
+  color: #444444 !important;
 }

文件差异内容过多而无法显示
+ 1 - 1
public/assets/css/backend.min.css


+ 1 - 1
public/assets/js/adminlte.js

@@ -304,7 +304,7 @@ function _init() {
                         $(".sidebar").slimscroll({
                             height: ($(window).height() - $(".main-header").height()) + "px",
                             color: "rgba(0,0,0,0.2)",
-                            size: "3px"
+                            size: "8px"
                         });
                     }
                 }

+ 85 - 5
public/assets/js/backend/index.js

@@ -215,6 +215,11 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
                 }
             });
 
+
+            var multiplenav = Config.fastadmin.multiplenav;
+            var firstnav = $("#firstnav .nav-addtabs");
+            var nav = multiplenav ? $("#secondnav .nav-addtabs") : firstnav;
+
             //刷新菜单事件
             $(document).on('refresh', '.sidebar-menu', function () {
                 Fast.api.ajax({
@@ -223,28 +228,103 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
                 }, function (data) {
                     $(".sidebar-menu li:not([data-rel='external'])").remove();
                     $(".sidebar-menu").prepend(data.menulist);
-                    $("#nav ul li[role='presentation'].active a").trigger('click');
+                    if (multiplenav) {
+                        firstnav.html(data.navlist);
+                    }
+                    $("li[role='presentation'].active a", nav).trigger('click');
                     return false;
                 }, function () {
                     return false;
                 });
             });
 
+            if (multiplenav) {
+                //一级菜单自适应
+                $(window).resize(function () {
+                    var siblingsWidth = 0;
+                    firstnav.siblings().each(function () {
+                        siblingsWidth += $(this).outerWidth();
+                    });
+                    firstnav.width(firstnav.parent().width() - siblingsWidth);
+                    firstnav.refreshAddtabs();
+                });
+
+                //点击顶部第一级菜单栏
+                firstnav.on("click", "li a", function () {
+                    $("li", firstnav).removeClass("active");
+                    $(this).closest("li").addClass("active");
+                    $(".sidebar-menu > li.treeview").addClass("hidden");
+                    if ($(this).attr("url") == "javascript:;") {
+                        var sonlist = $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "']");
+                        sonlist.removeClass("hidden");
+                        var last_id = $(this).attr("last-id");
+                        if (last_id) {
+                            $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "'] a[addtabs='" + last_id + "']").trigger('click');
+                        } else {
+                            $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "']:first > a").trigger('click');
+                        }
+                    } else {
+
+                    }
+                });
+
+                //点击左侧菜单栏
+                $(document).on('click', '.sidebar-menu li a[addtabs]', function (e) {
+                    var parents = $(this).parentsUntil("ul.sidebar-menu", "li");
+                    var top = parents[parents.length - 1];
+                    var pid = $(top).attr("pid");
+                    if (pid) {
+                        var obj = $("li a[addtabs=" + pid + "]", firstnav);
+                        var last_id = obj.attr("last-id");
+                        if (!last_id || last_id != pid) {
+                            obj.attr("last-id", $(this).attr("addtabs"));
+                            if (!obj.closest("li").hasClass("active")) {
+                                obj.trigger("click");
+                            }
+                        }
+                    }
+                });
+
+                var mobilenav = $(".mobilenav");
+                $("#firstnav .nav-addtabs li a").each(function(){
+                    mobilenav.append($(this).clone().addClass("btn btn-app"));
+                });
+
+                //点击移动端一级菜单
+                mobilenav.on("click", "a", function () {
+                    $("a", mobilenav).removeClass("active");
+                    $(this).addClass("active");
+                    $(".sidebar-menu > li.treeview").addClass("hidden");
+                    if ($(this).attr("url") == "javascript:;") {
+                        var sonlist = $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "']");
+                        sonlist.removeClass("hidden");
+                    }
+                });
+            }
+
             //这一行需要放在点击左侧链接事件之前
             var addtabs = Config.referer ? localStorage.getItem("addtabs") : null;
 
             //绑定tabs事件,如果需要点击强制刷新iframe,则请将iframeForceRefresh置为true
-            $('#nav').addtabs({iframeHeight: "100%", iframeForceRefresh: false});
+            nav.addtabs({iframeHeight: "100%", iframeForceRefresh: false, nav: nav});
+
             if ($("ul.sidebar-menu li.active a").size() > 0) {
                 $("ul.sidebar-menu li.active a").trigger("click");
             } else {
-                $("ul.sidebar-menu li a[url!='javascript:;']:first").trigger("click");
+                if (Config.fastadmin.multiplenav) {
+                    $("li:first > a", firstnav).trigger("click");
+                } else {
+                    $("ul.sidebar-menu li a[url!='javascript:;']:first").trigger("click");
+                }
             }
 
             //如果是刷新操作则直接返回刷新前的页面
             if (Config.referer) {
                 if (Config.referer === $(addtabs).attr("url")) {
                     var active = $("ul.sidebar-menu li a[addtabs=" + $(addtabs).attr("addtabs") + "]");
+                    if (multiplenav && active.size() == 0) {
+                        active = $("ul li a[addtabs='" + $(addtabs).attr("addtabs") + "']");
+                    }
                     if (active.size() > 0) {
                         active.trigger("click");
                     } else {
@@ -319,7 +399,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
                     if ($(this).data("menu") == 'show-submenu') {
                         $("ul.sidebar-menu").toggleClass("show-submenu");
                     } else {
-                        $(".nav-addtabs").toggleClass("disable-top-badge");
+                        nav.toggleClass("disable-top-badge");
                     }
                 });
 
@@ -365,7 +445,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
                 if ($('ul.sidebar-menu').hasClass('show-submenu')) {
                     $("[data-menu='show-submenu']").attr('checked', 'checked');
                 }
-                if ($('ul.nav-addtabs').hasClass('disable-top-badge')) {
+                if (nav.hasClass('disable-top-badge')) {
                     $("[data-menu='disable-top-badge']").attr('checked', 'checked');
                 }
 

+ 12 - 3
public/assets/js/bootstrap-table-commonsearch.js

@@ -193,7 +193,7 @@
                     }
                 } else {
                     value = $("[name='" + name + "']:checked", that.$commonsearch).val();
-                    value = (vObjCol && typeof vObjCol.process === 'function') ? vObjCol.process(obj.val()) : obj.val();
+                    value = (vObjCol && typeof vObjCol.process === 'function') ? vObjCol.process(value) : value;
                 }
             } else {
                 value = (vObjCol && typeof vObjCol.process === 'function') ? vObjCol.process(obj.val()) : obj.val();
@@ -297,7 +297,7 @@
 
         var that = this,
             html = [];
-        if(that.options.showSearch){
+        if (that.options.showSearch) {
             html.push(sprintf('<div class="columns-%s pull-%s" style="margin-top:10px;margin-bottom:10px;">', this.options.buttonsAlign, this.options.buttonsAlign));
             html.push(sprintf('<button class="btn btn-default%s' + '" type="button" name="commonSearch" title="%s">', that.options.iconSize === undefined ? '' : ' btn-' + that.options.iconSize, that.options.formatCommonSearch()));
             html.push(sprintf('<i class="%s %s"></i>', that.options.iconsPrefix, that.options.icons.commonSearchIcon))
@@ -320,7 +320,16 @@
         that.$container.on("click", "." + that.options.searchClass, function () {
             var obj = $("form [name='" + $(this).data("field") + "']", that.$commonsearch);
             if (obj.size() > 0) {
-                obj.val($(this).data("value"));
+                var value = $(this).data("value");
+                if (obj.is("select")) {
+                    console.log($("option[value='" + value + "']", obj));
+                    $("option[value='" + value + "']", obj).prop("selected", true);
+                } else if (obj.size() > 1) {
+                    $("form [name='" + $(this).data("field") + "'][value='" + value + "']", that.$commonsearch).prop("checked", true);
+                } else {
+                    obj.val(value);
+                }
+                obj.trigger("change");
                 $("form", that.$commonsearch).trigger("submit");
             }
         });

+ 6 - 6
public/assets/js/require-backend.js

@@ -1,10 +1,10 @@
 require.config({
     urlArgs: "v=" + requirejs.s.contexts._.config.config.site.version,
     packages: [{
-            name: 'moment',
-            location: '../libs/moment',
-            main: 'moment'
-        }
+        name: 'moment',
+        location: '../libs/moment',
+        main: 'moment'
+    }
     ],
     //在打包压缩时将会把include中的模块合并到主文件中
     include: ['css', 'layer', 'toastr', 'fast', 'backend', 'backend-init', 'table', 'form', 'dragsort', 'drag', 'drop', 'addtabs', 'selectpage'],
@@ -102,10 +102,10 @@ require.config({
             'moment/locale/zh-cn',
 //            'css!../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css',
         ],
-        'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
+//        'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css',],
         'bootstrap-select-lang': ['bootstrap-select'],
 //        'toastr': ['css!../libs/toastr/toastr.min.css'],
-        'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],
+        'jstree': ['css!../libs/jstree/dist/themes/default/style.css',],
         'plupload': {
             deps: ['../libs/plupload/js/moxie.min'],
             exports: "plupload"

文件差异内容过多而无法显示
+ 311 - 270
public/assets/js/require-backend.min.js


+ 2 - 2
public/assets/js/require-frontend.js

@@ -49,7 +49,7 @@ require.config({
         'template': '../libs/art-template/dist/template-native',
         'selectpage': '../libs/fastadmin-selectpage/selectpage',
         'citypicker': '../libs/city-picker/dist/js/city-picker.min',
-        'citypicker-data': '../libs/city-picker/dist/js/city-picker.data',
+        'citypicker-data': '../libs/city-picker/dist/js/city-picker.data'
     },
     // shim依赖配置
     shim: {
@@ -102,7 +102,7 @@ require.config({
             'moment/locale/zh-cn',
 //            'css!../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css',
         ],
-        'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
+//        'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
         'bootstrap-select-lang': ['bootstrap-select'],
 //        'toastr': ['css!../libs/toastr/toastr.min.css'],
         'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],

+ 2 - 2
public/assets/js/require-frontend.min.js

@@ -63,7 +63,7 @@ require.config({
         'template': '../libs/art-template/dist/template-native',
         'selectpage': '../libs/fastadmin-selectpage/selectpage',
         'citypicker': '../libs/city-picker/dist/js/city-picker.min',
-        'citypicker-data': '../libs/city-picker/dist/js/city-picker.data',
+        'citypicker-data': '../libs/city-picker/dist/js/city-picker.data'
     },
     // shim依赖配置
     shim: {
@@ -116,7 +116,7 @@ require.config({
             'moment/locale/zh-cn',
 //            'css!../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css',
         ],
-        'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
+//        'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
         'bootstrap-select-lang': ['bootstrap-select'],
 //        'toastr': ['css!../libs/toastr/toastr.min.css'],
         'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],

+ 13 - 5
public/assets/js/require-table.js

@@ -372,7 +372,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                 image: function (value, row, index) {
                     value = value ? value : '/assets/img/blank.gif';
                     var classname = typeof this.classname !== 'undefined' ? this.classname : 'img-sm img-center';
-                    return '<img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" />';
+                    return '<a href="' + Fast.api.cdnurl(value) + '" target="_blank"><img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" /></a>';
                 },
                 images: function (value, row, index) {
                     value = value === null ? '' : value.toString();
@@ -381,7 +381,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     var html = [];
                     $.each(arr, function (i, value) {
                         value = value ? value : '/assets/img/blank.gif';
-                        html.push('<img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" />');
+                        html.push('<a href="' + Fast.api.cdnurl(value) + '" target="_blank"><img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" /></a>');
                     });
                     return html.join(' ');
                 },
@@ -394,9 +394,12 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     }
                     value = value === null ? '' : value.toString();
                     var color = value && typeof colorArr[value] !== 'undefined' ? colorArr[value] : 'primary';
-                    value = value.charAt(0).toUpperCase() + value.slice(1);
+                    var newValue = value.charAt(0).toUpperCase() + value.slice(1);
                     //渲染状态
-                    var html = '<span class="text-' + color + '"><i class="fa fa-circle"></i> ' + __(value) + '</span>';
+                    var html = '<span class="text-' + color + '"><i class="fa fa-circle"></i> ' + __(newValue) + '</span>';
+                    if (this.operate != false) {
+                        html = '<a href="javascript:;" class="searchit" data-toggle="tooltip" title="' + __('Click to search %s', __(newValue)) + '" data-field="' + this.field + '" data-value="' + value + '">' + html + '</a>';
+                    }
                     return html;
                 },
                 url: function (value, row, index) {
@@ -442,7 +445,12 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     return Table.api.formatter.flag.call(this, value, row, index);
                 },
                 datetime: function (value, row, index) {
-                    return value ? Moment(parseInt(value) * 1000).format("YYYY-MM-DD HH:mm:ss") : __('None');
+                    var datetimeFormat = typeof this.datetimeFormat === 'undefined' ? 'YYYY-MM-DD HH:mm:ss' : this.datetimeFormat;
+                    if (isNaN(value)) {
+                        return value ? Moment(value).format(datetimeFormat) : __('None');
+                    } else {
+                        return value ? Moment(parseInt(value) * 1000).format(datetimeFormat) : __('None');
+                    }
                 },
                 operate: function (value, row, index) {
                     var table = this.table;

文件差异内容过多而无法显示
+ 895 - 643
public/assets/less/backend.less