Browse Source

新增视图中根据权限控制元素显示
新增生产模式下新的异常提示页
新增Table.api.formatter权限控制
新增Table.api.formatter的buttons参数
将后台common.php中的通过方法移动到全局可调用
修复iOS在底部弹出窗时的错误
修复iOS下文本框自动变大的问题
修改CRUD基础index模板
移除Date::cron方法
修复后台登录一处JS报错
修复Fast.api.ajax发生错误时提示不正确的问题

Karson 8 years ago
parent
commit
d7bc9ffcc2

+ 1 - 1
application/admin/command/Crud.php

@@ -684,7 +684,7 @@ class Crud extends Command
             }
 
             //JS最后一列加上操作列
-            $javascriptList[] = str_repeat(" ", 24) . "{field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}";
+            $javascriptList[] = str_repeat(" ", 24) . "{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}";
             $addList = implode("\n", array_filter($addList));
             $editList = implode("\n", array_filter($editList));
             $javascriptList = implode(",\n", array_filter($javascriptList));

+ 5 - 2
application/admin/command/Crud/stubs/index.stub

@@ -7,7 +7,7 @@
                 <div class="widget-body no-padding">
                     <div id="toolbar" class="toolbar">
                         {:build_toolbar()}
-                        <div class="dropdown btn-group">
+                        <div class="dropdown btn-group {:$auth->check('{%controllerUrl%}/multi')?'':'hide'}">
                             <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
                             <ul class="dropdown-menu text-left" role="menu">
                                 <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
@@ -15,7 +15,10 @@
                             </ul>
                         </div>
                     </div>
-                    <table id="table" class="table table-striped table-bordered table-hover" width="100%">
+                    <table id="table" class="table table-striped table-bordered table-hover" 
+                           data-operate-edit="{:$auth->check('{%controllerUrl%}/edit')}" 
+                           data-operate-del="{:$auth->check('{%controllerUrl%}/del')}" 
+                           width="100%">
                     </table>
                 </div>
             </div>

+ 11 - 87
application/admin/common.php

@@ -88,19 +88,27 @@ function build_category_select($name, $type, $selected = null, $attr = [], $head
  */
 function build_toolbar($btns = NULL, $attr = [])
 {
-    $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'delete'];
+    $auth = \app\admin\library\Auth::instance();
+    $controller = strtolower(think\Request::instance()->controller());
+    $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del'];
     $btns = is_array($btns) ? $btns : explode(',', $btns);
+    $index = array_search('delete', $btns);
+    if ($index !== FALSE)
+    {
+        $btns[$index] = 'del';
+    }
     $btnAttr = [
         'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', ''],
         'add'     => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add')],
         'edit'    => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit')],
-        'delete'  => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete')],
+        'del'     => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete')],
     ];
     $btnAttr = array_merge($btnAttr, $attr);
     $html = [];
     foreach ($btns as $k => $v)
     {
-        if (!isset($btnAttr[$v]))
+        //如果未定义或没有权限
+        if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}")))
         {
             continue;
         }
@@ -135,87 +143,3 @@ function build_heading($title = NULL, $content = NULL)
         return '';
     return '<div class="panel-heading"><div class="panel-lead"><em>' . $title . '</em>' . $content . '</div></div>';
 }
-
-/**
- * 判断文件或文件夹是否可写
- * @param	string
- * @return	bool
- */
-function is_really_writable($file)
-{
-    if (DIRECTORY_SEPARATOR === '/')
-    {
-        return is_writable($file);
-    }
-    if (is_dir($file))
-    {
-        $file = rtrim($file, '/') . '/' . md5(mt_rand());
-        if (($fp = @fopen($file, 'ab')) === FALSE)
-        {
-            return FALSE;
-        }
-        fclose($fp);
-        @chmod($file, 0777);
-        @unlink($file);
-        return TRUE;
-    }
-    elseif (!is_file($file) OR ( $fp = @fopen($file, 'ab')) === FALSE)
-    {
-        return FALSE;
-    }
-    fclose($fp);
-    return TRUE;
-}
-
-/**
- * 删除文件夹
- * @param string $dirname
- * @return boolean
- */
-function rmdirs($dirname)
-{
-    if (!is_dir($dirname))
-        return false;
-    $files = new RecursiveIteratorIterator(
-            new RecursiveDirectoryIterator($dirname, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST
-    );
-
-    foreach ($files as $fileinfo)
-    {
-        $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
-        $todo($fileinfo->getRealPath());
-    }
-    @rmdir($dirname);
-    return true;
-}
-
-/**
- * 复制文件夹
- * @param string $source 源文件夹
- * @param string $dest 目标文件夹
- */
-function copydirs($source, $dest)
-{
-    if (!is_dir($dest))
-    {
-        mkdir($dest, 0755);
-    }
-    foreach (
-    $iterator = new RecursiveIteratorIterator(
-    new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST) as $item
-    )
-    {
-        if ($item->isDir())
-        {
-            $sontDir = $dest . DS . $iterator->getSubPathName();
-            if (!is_dir($sontDir))
-            {
-                mkdir($sontDir);
-            }
-        }
-        else
-        {
-            copy($item, $dest . DS . $iterator->getSubPathName());
-        }
-    }
-}

+ 1 - 1
application/admin/controller/Addon.php

@@ -46,7 +46,7 @@ class Addon extends Backend
         $name = $this->request->get("name");
         if (!$name)
         {
-            $this->error(__('Parameter %s can not be empty', $id ? 'id' : 'name'));
+            $this->error(__('Parameter %s can not be empty', $ids ? 'id' : 'name'));
         }
         if (!is_dir(ADDON_PATH . $name))
         {

+ 18 - 4
application/admin/controller/Category.php

@@ -28,7 +28,7 @@ class Category extends Backend
         $tree = Tree::instance();
         $tree->init($this->model->order('weigh desc,id desc')->select(), 'pid');
         $this->categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name');
-        $categorydata = [0 => ['type'=>'all', 'name'=>__('None')]];
+        $categorydata = [0 => ['type' => 'all', 'name' => __('None')]];
         foreach ($this->categorylist as $k => $v)
         {
             $categorydata[$v['id']] = $v;
@@ -45,11 +45,25 @@ class Category extends Backend
     {
         if ($this->request->isAjax())
         {
-
+            $search = $this->request->request("search");
             //构造父类select列表选项数据
-            $list = $this->categorylist;
+            $list = [];
+            if ($search)
+            {
+                foreach ($this->categorylist as $k => $v)
+                {
+                    if (stripos($v['name'], $search) !== false || stripos($v['nickname'], $search) !== false)
+                    {
+                        $list[] = $v;
+                    }
+                }
+            }
+            else
+            {
+                $list = $this->categorylist;
+            }
             $total = count($list);
-            $result = array("total" => 1, "rows" => $list);
+            $result = array("total" => $total, "rows" => $list);
 
             return json($result);
         }

+ 1 - 1
application/admin/view/common/meta.html

@@ -1,6 +1,6 @@
 <meta charset="utf-8">
 <title>{$title|default=''}</title>
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
 <meta name="renderer" content="webkit">
 
 <link rel="shortcut icon" href="__CDN__/assets/img/favicon.ico" />

+ 1 - 1
application/admin/view/general/profile/index.html

@@ -43,7 +43,7 @@
                         <div class="profile-avatar-container">
                             <img class="profile-user-img img-responsive img-circle plupload" src="__CDN__{$admin.avatar}" alt="">
                             <div class="profile-avatar-text img-circle">{:__('Click to edit')}</div>
-                            <button id="plupload-avatar" class="plupload" data-input-id="c-avatar" data-after-upload="changeavatar"><i class="fa fa-upload"></i> {:__('Upload')}</button>
+                            <button id="plupload-avatar" class="plupload" data-input-id="c-avatar"><i class="fa fa-upload"></i> {:__('Upload')}</button>
                         </div>
                         
                         <h3 class="profile-username text-center">{$admin.username}</h3>

+ 104 - 0
application/common.php

@@ -94,3 +94,107 @@ if (!function_exists('cdnurl'))
     }
 
 }
+
+
+if (!function_exists('is_really_writable'))
+{
+
+    /**
+     * 判断文件或文件夹是否可写
+     * @param	string $file 文件或目录
+     * @return	bool
+     */
+    function is_really_writable($file)
+    {
+        if (DIRECTORY_SEPARATOR === '/')
+        {
+            return is_writable($file);
+        }
+        if (is_dir($file))
+        {
+            $file = rtrim($file, '/') . '/' . md5(mt_rand());
+            if (($fp = @fopen($file, 'ab')) === FALSE)
+            {
+                return FALSE;
+            }
+            fclose($fp);
+            @chmod($file, 0777);
+            @unlink($file);
+            return TRUE;
+        }
+        elseif (!is_file($file) OR ( $fp = @fopen($file, 'ab')) === FALSE)
+        {
+            return FALSE;
+        }
+        fclose($fp);
+        return TRUE;
+    }
+
+}
+
+if (!function_exists('rmdirs'))
+{
+
+    /**
+     * 删除文件夹
+     * @param string $dirname 目录
+     * @param bool $withself 是否删除自身
+     * @return boolean
+     */
+    function rmdirs($dirname, $withself = true)
+    {
+        if (!is_dir($dirname))
+            return false;
+        $files = new RecursiveIteratorIterator(
+                new RecursiveDirectoryIterator($dirname, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST
+        );
+
+        foreach ($files as $fileinfo)
+        {
+            $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
+            $todo($fileinfo->getRealPath());
+        }
+        if ($withself)
+        {
+            @rmdir($dirname);
+        }
+        return true;
+    }
+
+}
+
+if (!function_exists('copydirs'))
+{
+
+    /**
+     * 复制文件夹
+     * @param string $source 源文件夹
+     * @param string $dest 目标文件夹
+     */
+    function copydirs($source, $dest)
+    {
+        if (!is_dir($dest))
+        {
+            mkdir($dest, 0755);
+        }
+        foreach (
+        $iterator = new RecursiveIteratorIterator(
+        new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST) as $item
+        )
+        {
+            if ($item->isDir())
+            {
+                $sontDir = $dest . DS . $iterator->getSubPathName();
+                if (!is_dir($sontDir))
+                {
+                    mkdir($sontDir);
+                }
+            }
+            else
+            {
+                copy($item, $dest . DS . $iterator->getSubPathName());
+            }
+        }
+    }
+
+}

+ 6 - 1
application/common/behavior/Common.php

@@ -26,11 +26,16 @@ class Common
         {
             Config::set('upload.cdnurl', $cdnurl);
         }
-        // 如果是调试模式将version置为当前的时间戳可避免缓存
         if (Config::get('app_debug'))
         {
+            // 如果是调试模式将version置为当前的时间戳可避免缓存
             Config::set('site.version', time());
         }
+        else
+        {
+            // 如果是开发模式修改异常页的模板
+            Config::set('exception_tmpl', APP_PATH . 'common' . DS . 'view' . DS . 'tpl' . DS . 'think_exception.tpl');
+        }
     }
 
 }

+ 2 - 0
application/common/controller/Backend.php

@@ -165,6 +165,8 @@ class Backend extends Controller
         $this->assign('site', $site);
         //渲染配置信息
         $this->assign('config', $config);
+        //渲染权限对象
+        $this->assign('auth', $this->auth);
         //渲染管理员对象
         $this->assign('admin', Session::get('admin'));
     }

+ 74 - 0
application/common/view/tpl/think_exception.tpl

@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+        <title>发生错误</title>
+        <meta name="robots" content="noindex,nofollow" />
+        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+        <style>
+            * {-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;}
+            html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,caption,article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,time,mark,audio,video {margin:0;padding:0;border:0;outline:0;vertical-align:baseline;background:transparent;}
+            article,aside,details,figcaption,figure,footer,header,hgroup,nav,section {display:block;}
+            html {font-size:16px;line-height:24px;width:100%;height:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;overflow-y:scroll;overflow-x:hidden;}
+            img {vertical-align:middle;max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;}
+            body {min-height:100%;background:#edf1f4;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",微软雅黑,Arial,sans-serif;}
+            .clearfix {clear:both;zoom:1;}
+            .clearfix:before,.clearfix:after {content:"\0020";display:block;height:0;visibility:hidden;}
+            .clearfix:after {clear:both;}
+            body.error-page-wrapper,.error-page-wrapper.preview {background-position:center center;background-repeat:no-repeat;background-size:cover;position:relative;}
+            .error-page-wrapper .content-container {border-radius:2px;text-align:center;box-shadow:1px 1px 1px rgba(99,99,99,0.1);padding:50px;background-color:#fff;width:100%;max-width:560px;position:absolute;left:50%;top:50%;margin-top:-220px;margin-left:-280px;}
+            .error-page-wrapper .content-container.in {left:0px;opacity:1;}
+            .error-page-wrapper .head-line {transition:color .2s linear;font-size:40px;line-height:60px;letter-spacing:-1px;margin-bottom:20px;color:#777;}
+            .error-page-wrapper .subheader {transition:color .2s linear;font-size:32px;line-height:46px;color:#494949;}
+            .error-page-wrapper .hr {height:1px;background-color:#eee;width:80%;max-width:350px;margin:25px auto;}
+            .error-page-wrapper .context {transition:color .2s linear;font-size:16px;line-height:27px;color:#aaa;}
+            .error-page-wrapper .context p {margin:0;}
+            .error-page-wrapper .context p:nth-child(n+2) {margin-top:16px;}
+            .error-page-wrapper .buttons-container {margin-top:35px;overflow:hidden;}
+            .error-page-wrapper .buttons-container a {transition:text-indent .2s ease-out,color .2s linear,background-color .2s linear;text-indent:0px;font-size:14px;text-transform:uppercase;text-decoration:none;color:#fff;background-color:#2ecc71;border-radius:99px;padding:8px 0 8px;text-align:center;display:inline-block;overflow:hidden;position:relative;width:45%;}
+            .error-page-wrapper .buttons-container a:hover {text-indent:15px;}
+            .error-page-wrapper .buttons-container a:nth-child(1) {float:left;}
+            .error-page-wrapper .buttons-container a:nth-child(2) {float:right;}
+            @media screen and (max-width:580px) {
+                .error-page-wrapper {padding:30px 5%;}
+                .error-page-wrapper .content-container {padding:37px;position:static;left:0;margin-top:0;margin-left:0;}
+                .error-page-wrapper .head-line {font-size:36px;}
+                .error-page-wrapper .subheader {font-size:27px;line-height:37px;}
+                .error-page-wrapper .hr {margin:30px auto;width:215px;}
+            }
+            @media screen and (max-width:450px) {
+                .error-page-wrapper {padding:30px;}
+                .error-page-wrapper .head-line {font-size:32px;}
+                .error-page-wrapper .hr {margin:25px auto;width:180px;}
+                .error-page-wrapper .context {font-size:15px;line-height:22px;}
+                .error-page-wrapper .context p:nth-child(n+2) {margin-top:10px;}
+                .error-page-wrapper .buttons-container {margin-top:29px;}
+                .error-page-wrapper .buttons-container a {float:none !important;width:65%;margin:0 auto;font-size:13px;padding:9px 0;}
+                .error-page-wrapper .buttons-container a:nth-child(2) {margin-top:12px;}
+            }
+        </style>
+    </head>
+    <body class="error-page-wrapper">
+
+        <div class="content-container">
+            <div class="head-line">
+                <img src="/assets/img/error.svg" alt="" width="120" />
+            </div>
+            <div class="subheader">
+                <?php echo htmlentities($message); ?>
+            </div>
+            <div class="hr"></div>
+            <div class="context">
+
+                <p>
+                    你可以返回上一页重试,或直接向我们反馈错误报告
+                </p>
+
+            </div>
+            <div class="buttons-container">
+                <a href="<?php echo url('/');?>">返回主页</a>
+                <a href="<?php echo url('/');?>">反馈错误</a>
+            </div>
+        </div>
+    </body>
+</html>

+ 1 - 1
application/config.php

@@ -146,7 +146,7 @@ return [
     // 异常页面的模板文件
     'exception_tmpl'         => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
     // 错误显示信息,非调试模式有效
-    'error_message'          => '页面错误!请稍后再试~',
+    'error_message'          => '你所浏览的页面暂时无法访问',
     // 显示错误信息
     'show_error_msg'         => false,
     // 异常处理handle类 留空使用 \think\exception\Handle

+ 1 - 1
application/index/controller/Index.php

@@ -21,7 +21,7 @@ class Index extends Frontend
 
     public function news()
     {
-        $newslist = \app\common\model\Page::where('category_id', 1)->order('weigh', 'desc')->select();
+        $newslist = [];
         return jsonp(['newslist' => $newslist, 'new' => count($newslist), 'url' => 'http://www.fastadmin.net?ref=news']);
     }
 

+ 0 - 72
extend/fast/Date.php

@@ -157,78 +157,6 @@ class Date
     }
 
     /**
-     * 判断Unix时间是否满足Cron指定的执行条件
-     *
-     * @param string $cron Crontab格式
-     * @param string $time 时间,默认为当前时间
-     * @return boolean
-     */
-    public static function cron($cron, $time = null)
-    {
-        $time = is_null($time) ? time() : $time;
-        $cron_parts = explode(' ', $cron);
-        if (count($cron_parts) != 5)
-        {
-            return false;
-        }
-        list($min, $hour, $day, $mon, $week) = explode(' ', $cron);
-        $to_check = array('min' => 'i', 'hour' => 'G', 'day' => 'j', 'mon' => 'n', 'week' => 'w');
-        $ranges = array(
-            'min'  => '0-59',
-            'hour' => '0-23',
-            'day'  => '1-31',
-            'mon'  => '1-12',
-            'week' => '0-6',
-        );
-
-        foreach ($to_check as $part => $c)
-        {
-            $val = $$part;
-            $values = [];
-            if (strpos($val, '/') !== false)
-            {
-                //Get the range and step
-                list($range, $steps) = explode('/', $val);
-                //Now get the start and stop
-                if ($range == '*')
-                {
-                    $range = $ranges[$part];
-                }
-                list($start, $stop) = explode('-', $range);
-                for ($i = $start; $i <= $stop; $i = $i + $steps)
-                {
-                    $values[] = $i;
-                }
-            }
-            else
-            {
-                $k = explode(',', $val);
-                foreach ($k as $v)
-                {
-                    if (strpos($v, '-') !== false)
-                    {
-                        list($start, $stop) = explode('-', $v);
-
-                        for ($i = $start; $i <= $stop; $i++)
-                        {
-                            $values[] = $i;
-                        }
-                    }
-                    else
-                    {
-                        $values[] = $v;
-                    }
-                }
-            }
-            if (!in_array(date($c, $time), $values) and ( strval($val) != '*'))
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
      * 获取一个基于时间偏移的Unix时间戳
      *
      * @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year

+ 2 - 2
public/assets/js/backend/auth/admin.js

@@ -28,11 +28,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'email', title: __('Email')},
                         {field: 'status', title: __("Status"), formatter: Table.api.formatter.status},
                         {field: 'logintime', title: __('Login time'), formatter: Table.api.formatter.datetime},
-                        {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: function (value, row, index) {
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: function (value, row, index) {
                                 if(row.id == 1){
                                     return '';
                                 }
-                                return Table.api.formatter.operate.call(this, value, row, index, table);
+                                return Table.api.formatter.operate.call(this, value, row, index);
                             }}
                     ]
                 ]

+ 14 - 29
public/assets/js/backend/auth/adminlog.js

@@ -24,11 +24,21 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'id', title: 'ID', operate: false},
                         {field: 'username', title: __('Username'), formatter: Table.api.formatter.search},
                         {field: 'title', title: __('Title'), operate: 'LIKE %...%', placeholder: '模糊搜索,*表示任意字符', style: 'width:200px'},
-                        {field: 'url', title: __('Url'), align: 'left', formatter: Controller.api.formatter.url},
-                        {field: 'ip', title: __('IP'), events: Controller.api.events.ip, formatter: Controller.api.formatter.ip},
-                        {field: 'browser', title: __('Browser'), operate: false, events: Controller.api.events.browser, formatter: Controller.api.formatter.browser},
+                        {field: 'url', title: __('Url'), align: 'left', formatter: Table.api.formatter.url},
+                        {field: 'ip', title: __('IP'), events: Table.api.events.ip, formatter: Table.api.formatter.search},
+                        {field: 'browser', title: __('Browser'), operate: false, formatter: Controller.api.formatter.browser},
                         {field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime, operate: 'BETWEEN', type: 'datetime', addclass: 'datetimepicker', data: 'data-date-format="YYYY-MM-DD HH:mm:ss"'},
-                        {field: 'operate', title: __('Operate'), events: Controller.api.events.operate, formatter: Controller.api.formatter.operate}
+                        {field: 'operate', title: __('Operate'), table: table,
+                            events: Table.api.events.operate,
+                            buttons: [{
+                                    name: 'detail',
+                                    text: 'Detail',
+                                    icon: 'fa fa-list',
+                                    classname: 'btn btn-info btn-xs btn-detail btn-dialog',
+                                    url: 'auth/adminlog/detail'
+                                }],
+                            formatter: Table.api.formatter.operate
+                        }
                     ]
                 ]
             });
@@ -41,35 +51,10 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 Form.api.bindevent($("form[role=form]"));
             },
             formatter: {
-                url: function (value, row, index) {
-                    return '<div class="input-group input-group-sm" style="width:250px;"><input type="text" class="form-control input-sm" value="' + value + '"><span class="input-group-btn input-group-sm"><a href="' + value + '" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-link"></i></a></span></div>';
-                },
-                ip: function (value, row, index) {
-                    return '<a class="btn btn-xs btn-ip bg-success"><i class="fa fa-map-marker"></i> ' + value + '</a>';
-                },
                 browser: function (value, row, index) {
                     return '<a class="btn btn-xs btn-browser">' + row.useragent.split(" ")[0] + '</a>';
                 },
-                operate: function (value, row, index) {
-                    return '<a class="btn btn-info btn-xs btn-detail">' + __('Detail') + '</a> '
-                            + Table.api.formatter.operate(value, row, index, $("#table"));
-                },
             },
-            events: {
-                ip: {
-                    'click .btn-ip': function (e, value, row, index) {
-                        var options = $("#table").bootstrapTable('getOptions');
-                        //这里我们手动将数据填充到表单然后提交
-                        $("#commonSearchContent_" + options.idTable + " form [name='ip']").val(value);
-                        $("#commonSearchContent_" + options.idTable + " form").trigger('submit');
-                    }
-                },
-                operate: $.extend({
-                    'click .btn-detail': function (e, value, row, index) {
-                        Backend.api.open('auth/adminlog/detail/ids/' + row['id'], __('Detail'));
-                    }
-                }, Table.api.events.operate)
-            }
         }
     };
     return Controller;

+ 2 - 2
public/assets/js/backend/auth/group.js

@@ -41,11 +41,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'], function (
                         {field: 'pid', title: __('Parent')},
                         {field: 'name', title: __('Name'), align: 'left'},
                         {field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
-                        {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: function (value, row, index) {
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: function (value, row, index) {
                                 if (row.id == 1) {
                                     return '';
                                 }
-                                return Table.api.formatter.operate.call(this, value, row, index, table);
+                                return Table.api.formatter.operate.call(this, value, row, index);
                             }}
                     ]
                 ],

+ 1 - 1
public/assets/js/backend/auth/rule.js

@@ -32,7 +32,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                         {field: 'status', title: __('Status'), formatter: Table.api.formatter.status},
                         {field: 'ismenu', title: __('Ismenu'), align: 'center', formatter: Controller.api.formatter.menu},
                         {field: 'id', title: '<a href="javascript:;" class="btn btn-success btn-xs btn-toggle"><i class="fa fa-chevron-up"></i></a>', operate: false, formatter: Controller.api.formatter.subnode},
-                        {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]
                 ],
                 pagination: false,

+ 3 - 1
public/assets/js/backend/category.js

@@ -22,6 +22,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                 escape: false,
                 pk: 'id',
                 sortName: 'weigh',
+                pagination: false,
+                commonSearch: false,
                 columns: [
                     [
                         {checkbox: true},
@@ -33,7 +35,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'image', title: __('Image'), operate: false, formatter: Table.api.formatter.image},
                         {field: 'weigh', title: __('Weigh'), operate: false},
                         {field: 'status', title: __('Status'), operate: false, formatter: Table.api.formatter.status},
-                        {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]
                 ]
             });

+ 1 - 1
public/assets/js/backend/general/attachment.js

@@ -33,7 +33,7 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
                         {field: 'filesize', title: __('Filesize')},
                         {field: 'mimetype', title: __('Mimetype')},
                         {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime},
-                        {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]
                 ],
             });

+ 1 - 1
public/assets/js/backend/general/config.js

@@ -29,7 +29,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'intro', title: __('Intro')},
                         {field: 'group', title: __('Group')},
                         {field: 'type', title: __('Type')},
-                        {field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]
                 ]
             });

+ 9 - 12
public/assets/js/backend/general/profile.js

@@ -26,7 +26,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'upload'], function (
                     [
                         {field: 'id', title: 'ID'},
                         {field: 'title', title: __('Title')},
-                        {field: 'url', title: __('Url'), align: 'left', formatter: Controller.api.formatter.url},
+                        {field: 'url', title: __('Url'), align: 'left', formatter: Table.api.formatter.url},
                         {field: 'ip', title: __('ip')},
                         {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime},
                     ]
@@ -37,24 +37,21 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'upload'], function (
             // 为表格绑定事件
             Table.api.bindevent(table);//当内容渲染完成后
 
+            // 给上传按钮添加上传成功事件
+            $("#plupload-avatar").data("upload-success", function (data) {
+                var url = Backend.api.cdnurl(data.url);
+                $(".profile-user-img").prop("src", url);
+                Toastr.success("上传成功!");
+            });
+            
+            // 给表单绑定事件
             Form.api.bindevent($("#update-form"), function () {
                 $("input[name='row[password]']").val('');
                 var url = Backend.api.cdnurl($("#c-avatar").val());
                 top.window.$(".user-panel .image img,.user-menu > a > img,.user-header > img").prop("src", url);
                 return true;
             });
-            Upload.api.custom.changeavatar = function (response) {
-                var url = Backend.api.cdnurl(response.url);
-                $(".profile-user-img").prop("src", url);
-            };
         },
-        api: {
-            formatter: {
-                url: function (value, row, index) {
-                    return '<div class="input-group input-group-sm" style="width:250px;"><input type="text" class="form-control input-sm" value="' + value + '"><span class="input-group-btn input-group-sm"><a href="' + value + '" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-link"></i></a></span></div>';
-                },
-            },
-        }
     };
     return Controller;
 });

+ 1 - 1
public/assets/js/backend/index.js

@@ -376,7 +376,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
             }
 
             //让错误提示框居中
-            Backend.config.toastr.positionClass = "toast-top-center";
+            Fast.config.toastr.positionClass = "toast-top-center";
 
             //本地验证未通过时提示
             $("#login-form").data("validator-options", {

+ 3 - 3
public/assets/js/fast.js

@@ -74,9 +74,9 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
                             Fast.events.onAjaxError(ret, error);
                         }
                     },
-                    error: function (err) {
+                    error: function (xhr) {
                         Layer.close(index);
-                        var ret = {code: err.code, msg: err.message, data: null};
+                        var ret = {code: xhr.status, msg: xhr.statusText, data: null};
                         Fast.events.onAjaxError(ret, error);
                     }
                 }, options);
@@ -161,7 +161,7 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
                 }, options ? options : {});
                 if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && top.$(".tab-pane.active").size() > 0) {
                     options.area = [top.$(".tab-pane.active").width() + "px", top.$(".tab-pane.active").height() + "px"];
-                    options.offset = "lt";
+                    options.offset = [ top.$(".tab-pane.active").scrollTop() + "px", "0px"];
                 }
                 Layer.open(options);
                 return false;

+ 33 - 28
public/assets/js/require-backend.min.js

@@ -1999,9 +1999,9 @@ define('fast',['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, u
                             Fast.events.onAjaxError(ret, error);
                         }
                     },
-                    error: function (err) {
+                    error: function (xhr) {
                         Layer.close(index);
-                        var ret = {code: err.code, msg: err.message, data: null};
+                        var ret = {code: xhr.status, msg: xhr.statusText, data: null};
                         Fast.events.onAjaxError(ret, error);
                     }
                 }, options);
@@ -2086,7 +2086,7 @@ define('fast',['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, u
                 }, options ? options : {});
                 if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && top.$(".tab-pane.active").size() > 0) {
                     options.area = [top.$(".tab-pane.active").width() + "px", top.$(".tab-pane.active").height() + "px"];
-                    options.offset = "lt";
+                    options.offset = [ top.$(".tab-pane.active").scrollTop() + "px", "0px"];
                 }
                 Layer.open(options);
                 return false;
@@ -7812,7 +7812,6 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                 table.on('post-body.bs.table', function (e, settings, json, xhr) {
                     $(Table.config.refreshbtn, toolbar).find(".fa").removeClass("fa-spin");
                     $(Table.config.disabledbtn, toolbar).toggleClass('disabled', true);
-
                     if ($(Table.config.firsttd, table).find("input[type='checkbox'][data-index]").size() > 0) {
                         // 挺拽选择,需要重新绑定事件
                         require(['drag', 'drop'], function () {
@@ -7841,18 +7840,15 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                         });
                     }
                 });
-
                 // 处理选中筛选框后按钮的状态统一变更
                 table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table fa.event.check', function () {
                     var ids = Table.api.selectedids(table);
                     $(Table.config.disabledbtn, toolbar).toggleClass('disabled', !ids.length);
                 });
-
                 // 刷新按钮事件
                 $(toolbar).on('click', Table.config.refreshbtn, function () {
                     table.bootstrapTable('refresh');
                 });
-
                 // 添加按钮事件
                 $(toolbar).on('click', Table.config.addbtn, function () {
                     var ids = Table.api.selectedids(table);
@@ -7941,9 +7937,7 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                                 Layer.close(index);
                             }
                     );
-
                 });
-
                 var id = table.attr("id");
                 Table.list[id] = table;
                 return table;
@@ -7992,6 +7986,11 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                     }
                 }
             },
+            auth: {
+                check: function (name) {
+
+                }
+            },
             // 单元格数据格式化
             formatter: {
                 icon: function (value, row, index) {
@@ -8071,26 +8070,32 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr
                 datetime: function (value, row, index) {
                     return value ? Moment(parseInt(value) * 1000).format("YYYY-MM-DD HH:mm:ss") : __('None');
                 },
-                operate: function (value, row, index, table) {
-                    var showweigh = true;
-                    var showedit = true;
-                    var showdel = true;
-                    if (typeof table != 'undefined') {
-                        var options = table.bootstrapTable('getOptions');
-                        if (options.extend.del_url == '')
-                            showdel = false;
-                        if (options.extend.edit_url == '')
-                            showedit = false;
-                    }
-                    showweigh = typeof row[Table.config.dragsortfield] != 'undefined' ? true : false;
-                    //行操作
+                operate: function (value, row, index) {
+                    var table = this.table;
+                    // 操作配置
+                    var options = table ? table.bootstrapTable('getOptions') : {};
+                    // 默认按钮组
+                    var buttons = $.extend([], this.buttons || []);
+                    buttons.push({name: 'dragsort', icon: 'fa fa-arrows', classname: 'btn btn-xs btn-primary btn-dragsort'});
+                    buttons.push({name: 'edit', icon: 'fa fa-pencil', classname: 'btn btn-xs btn-success btn-editone'});
+                    buttons.push({name: 'del', icon: 'fa fa-trash', classname: 'btn btn-xs btn-danger btn-delone'});
                     var html = [];
-                    if (showweigh)
-                        html.push('<a href="javascript:;" class="btn btn-primary btn-dragsort btn-xs"><i class="fa fa-arrows"></i></a>');
-                    if (showedit)
-                        html.push('<a href="javascript:;" class="btn btn-success btn-editone btn-xs"><i class="fa fa-pencil"></i></a>');
-                    if (showdel)
-                        html.push('<a href="javascript:;" class="btn btn-danger btn-delone btn-xs"><i class="fa fa-trash"></i></a>');
+                    $.each(buttons, function (i, j) {
+                        var attr = table.data("operate-" + j.name);
+                        if ((typeof attr === 'undefined' || attr) || (j.name === 'dragsort' && typeof row[Table.config.dragsortfield] == 'undefined')) {
+                            if (['add', 'edit', 'del', 'multi'].indexOf(j.name) > -1 && !options.extend[j.name + "_url"]) {
+                                return true;
+                            }
+                            //自动加上ids
+                            j.url = j.url ? j.url + (j.url.match(/(\?|&)+/) ? "&ids=" : "/ids/") + row[options.pk] : '';
+                            url = j.url ? Fast.api.fixurl(j.url) : 'javascript:;';
+                            classname = j.classname ? j.classname : 'btn-primary btn-' + name + 'one';
+                            icon = j.icon ? j.icon : '';
+                            text = j.text ? j.text : '';
+                            title = j.title ? j.title : text;
+                            html.push('<a href="' + url + '" class="' + classname + '" title="' + title + '"><i class="' + icon + '"></i>' + (text ? ' ' + text : '') + '</a>');
+                        }
+                    });
                     return html.join(' ');
                 }
             },

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

@@ -2011,9 +2011,9 @@ define('fast',['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, u
                             Fast.events.onAjaxError(ret, error);
                         }
                     },
-                    error: function (err) {
+                    error: function (xhr) {
                         Layer.close(index);
-                        var ret = {code: err.code, msg: err.message, data: null};
+                        var ret = {code: xhr.status, msg: xhr.statusText, data: null};
                         Fast.events.onAjaxError(ret, error);
                     }
                 }, options);
@@ -2098,7 +2098,7 @@ define('fast',['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, u
                 }, options ? options : {});
                 if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && top.$(".tab-pane.active").size() > 0) {
                     options.area = [top.$(".tab-pane.active").width() + "px", top.$(".tab-pane.active").height() + "px"];
-                    options.offset = "lt";
+                    options.offset = [ top.$(".tab-pane.active").scrollTop() + "px", "0px"];
                 }
                 Layer.open(options);
                 return false;

+ 30 - 25
public/assets/js/require-table.js

@@ -113,7 +113,6 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                 table.on('post-body.bs.table', function (e, settings, json, xhr) {
                     $(Table.config.refreshbtn, toolbar).find(".fa").removeClass("fa-spin");
                     $(Table.config.disabledbtn, toolbar).toggleClass('disabled', true);
-
                     if ($(Table.config.firsttd, table).find("input[type='checkbox'][data-index]").size() > 0) {
                         // 挺拽选择,需要重新绑定事件
                         require(['drag', 'drop'], function () {
@@ -142,18 +141,15 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                         });
                     }
                 });
-
                 // 处理选中筛选框后按钮的状态统一变更
                 table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table fa.event.check', function () {
                     var ids = Table.api.selectedids(table);
                     $(Table.config.disabledbtn, toolbar).toggleClass('disabled', !ids.length);
                 });
-
                 // 刷新按钮事件
                 $(toolbar).on('click', Table.config.refreshbtn, function () {
                     table.bootstrapTable('refresh');
                 });
-
                 // 添加按钮事件
                 $(toolbar).on('click', Table.config.addbtn, function () {
                     var ids = Table.api.selectedids(table);
@@ -242,9 +238,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                                 Layer.close(index);
                             }
                     );
-
                 });
-
                 var id = table.attr("id");
                 Table.list[id] = table;
                 return table;
@@ -293,6 +287,11 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                     }
                 }
             },
+            auth: {
+                check: function (name) {
+
+                }
+            },
             // 单元格数据格式化
             formatter: {
                 icon: function (value, row, index) {
@@ -372,26 +371,32 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
                 datetime: function (value, row, index) {
                     return value ? Moment(parseInt(value) * 1000).format("YYYY-MM-DD HH:mm:ss") : __('None');
                 },
-                operate: function (value, row, index, table) {
-                    var showweigh = true;
-                    var showedit = true;
-                    var showdel = true;
-                    if (typeof table != 'undefined') {
-                        var options = table.bootstrapTable('getOptions');
-                        if (options.extend.del_url == '')
-                            showdel = false;
-                        if (options.extend.edit_url == '')
-                            showedit = false;
-                    }
-                    showweigh = typeof row[Table.config.dragsortfield] != 'undefined' ? true : false;
-                    //行操作
+                operate: function (value, row, index) {
+                    var table = this.table;
+                    // 操作配置
+                    var options = table ? table.bootstrapTable('getOptions') : {};
+                    // 默认按钮组
+                    var buttons = $.extend([], this.buttons || []);
+                    buttons.push({name: 'dragsort', icon: 'fa fa-arrows', classname: 'btn btn-xs btn-primary btn-dragsort'});
+                    buttons.push({name: 'edit', icon: 'fa fa-pencil', classname: 'btn btn-xs btn-success btn-editone'});
+                    buttons.push({name: 'del', icon: 'fa fa-trash', classname: 'btn btn-xs btn-danger btn-delone'});
                     var html = [];
-                    if (showweigh)
-                        html.push('<a href="javascript:;" class="btn btn-primary btn-dragsort btn-xs"><i class="fa fa-arrows"></i></a>');
-                    if (showedit)
-                        html.push('<a href="javascript:;" class="btn btn-success btn-editone btn-xs"><i class="fa fa-pencil"></i></a>');
-                    if (showdel)
-                        html.push('<a href="javascript:;" class="btn btn-danger btn-delone btn-xs"><i class="fa fa-trash"></i></a>');
+                    $.each(buttons, function (i, j) {
+                        var attr = table.data("operate-" + j.name);
+                        if ((typeof attr === 'undefined' || attr) || (j.name === 'dragsort' && typeof row[Table.config.dragsortfield] == 'undefined')) {
+                            if (['add', 'edit', 'del', 'multi'].indexOf(j.name) > -1 && !options.extend[j.name + "_url"]) {
+                                return true;
+                            }
+                            //自动加上ids
+                            j.url = j.url ? j.url + (j.url.match(/(\?|&)+/) ? "&ids=" : "/ids/") + row[options.pk] : '';
+                            url = j.url ? Fast.api.fixurl(j.url) : 'javascript:;';
+                            classname = j.classname ? j.classname : 'btn-primary btn-' + name + 'one';
+                            icon = j.icon ? j.icon : '';
+                            text = j.text ? j.text : '';
+                            title = j.title ? j.title : text;
+                            html.push('<a href="' + url + '" class="' + classname + '" title="' + title + '"><i class="' + icon + '"></i>' + (text ? ' ' + text : '') + '</a>');
+                        }
+                    });
                     return html.join(' ');
                 }
             },