From d878aeee2ed50cb744197e1ae92b82f18e56fe10 Mon Sep 17 00:00:00 2001
From: Karson <karsonzhang@163.com>
Date: Sun, 28 May 2017 23:01:09 +0800
Subject: [PATCH] 添加多图上传预览和选择 新增bootstrap-table的主键功能,可传入pk设定表的主键 修复管理员权限不刷新的BUG 修复提示未登录时,在登录后不跳转之前页面的BUG 修复在移动版部分按钮在视图的布局 修复头像不显示的BUG 修改UC默认关闭 修复plupload方法重复绑定事件的BUG 修复部分样式的显示错误 修复登录Token错误的BUG

---
 application/admin/command/Crud.php                    | 33 +++++++++++++++++++++++++++------
 application/admin/command/Crud/stubs/javascript.stub  |  1 +
 application/admin/controller/Index.php                |  6 +++---
 application/admin/controller/general/Attachment.php   | 26 ++++++++++++++++++++++++++
 application/admin/library/traits/Backend.php          |  7 +++----
 application/admin/view/common/header.html             |  4 ++--
 application/admin/view/general/attachment/select.html | 22 ++++++++++++++++++++++
 application/admin/view/general/profile/index.html     |  2 +-
 application/admin/view/page/add.html                  |  3 ++-
 application/admin/view/page/edit.html                 |  4 +++-
 application/common/controller/Backend.php             |  7 +++++--
 application/uc.php                                    |  2 +-
 bower.json                                            |  2 +-
 extend/fast/Auth.php                                  |  8 ++++++--
 public/assets/css/backend.css                         | 28 ++++++++++++++++++++++++++--
 public/assets/js/backend/general/attachment.js        | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 public/assets/js/backend/index.js                     |  6 +++---
 public/assets/js/require-form.js                      | 30 ++++++++++++++++++++++++++++++
 public/assets/js/require-table.js                     | 23 ++++++++++++++---------
 public/assets/js/require-upload.js                    | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 public/assets/less/backend.less                       | 31 +++++++++++++++++++++++++++++--
 21 files changed, 326 insertions(+), 49 deletions(-)
 create mode 100644 application/admin/view/general/attachment/select.html

diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php
index 606c922..05da39f 100644
--- a/application/admin/command/Crud.php
+++ b/application/admin/command/Crud.php
@@ -31,7 +31,7 @@ class Crud extends Command
     {
         $adminPath = dirname(__DIR__) . DS;
         //表名
-        $table = $input->getOption('table') ? : '';
+        $table = $input->getOption('table') ?: '';
         //自定义控制器
         $controller = $input->getOption('controller');
         //自定义模型
@@ -96,6 +96,7 @@ class Crud extends Command
 
         //从数据库中获取表字段信息
         $columnList = Db::query("SELECT * FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND table_name = ? ORDER BY ORDINAL_POSITION", [$dbname, $tableName]);
+
         $fields = [];
         foreach ($columnList as $k => $v)
         {
@@ -109,6 +110,20 @@ class Crud extends Command
         $field = 'id';
         $order = 'id';
         $priDefined = FALSE;
+        $prikey = '';
+        foreach ($columnList as $k => $v)
+        {
+            if ($v['COLUMN_KEY'] == 'PRI')
+            {
+                $prikey = $v['COLUMN_NAME'];
+                break;
+            }
+        }
+        if (!$prikey)
+        {
+            throw new Exception('Primary key not found!');
+        }
+        $order = $prikey;
 
         try
         {
@@ -161,7 +176,7 @@ class Crud extends Command
                         if ($v['DATA_TYPE'] == 'set')
                         {
                             $attrArr['multiple'] = '';
-                            $fieldName.="[]";
+                            $fieldName .= "[]";
                         }
                         $attrStr = $this->getArrayString($attrArr);
                         $itemArr = $this->getLangArray($itemArr, FALSE);
@@ -209,7 +224,7 @@ class Crud extends Command
                     }
                     else if ($inputType == 'checkbox')
                     {
-                        $fieldName.="[]";
+                        $fieldName .= "[]";
                         $itemArr = $this->getLangArray($itemArr, FALSE);
                         $itemString = $this->getArrayString($itemArr);
                         $formAddElement = "{:build_checkboxs('{$fieldName}', [{$itemString}], '{$defaultValue}')}";
@@ -252,7 +267,7 @@ class Crud extends Command
                         $step = array_intersect($cssClassArr, ['typeahead', 'tagsinput']) ? 0 : $step;
                         $attrArr['class'] = implode(' ', $cssClassArr);
 
-                        $isUpload = substr($field, -4) == 'file' || substr($field, -5) == 'image' || substr($field, -6) == 'avatar' ? TRUE : FALSE;
+                        $isUpload = in_array(substr($field, -4), ['file']) || in_array(substr($field, -5), ['files', 'image']) || in_array(substr($field, -6), ['images', 'avatar']) || in_array(substr($field, -7), ['avatars']) ? TRUE : FALSE;
                         //如果是步长则加上步长
                         if ($step)
                         {
@@ -327,6 +342,7 @@ class Crud extends Command
                 'modelName'               => $modelName,
                 'tableComment'            => $tableComment,
                 'iconName'                => $iconName,
+                'pk'                      => $prikey,
                 'order'                   => $order,
                 'table'                   => $table,
                 'tableName'               => $tableName,
@@ -531,11 +547,16 @@ EOD;
      */
     protected function getImageUpload($field, $content)
     {
-        $filter = substr($field, -4) == 'avatar' || substr($field, -5) == 'image' ? 'data-mimetype="image/*"' : "";
+        $filter = substr($field, -4) == 'avatar' || substr($field, -5) == 'image' || substr($field, -6) == 'images' ? ' data-mimetype="image/*"' : "";
+        $multiple = substr($field, -1) == 's' ? ' data-multiple="true"' : ' data-multiple="false"';
+        $preview = $filter ? ' data-preview-id="p-' . $field . '"' : '';
+        $previewcontainer = $preview ? '<ul class="row list-inline plupload-preview" id="p-' . $field . '"></ul>' : '';
         return <<<EOD
 <div class="form-inline">
                 {$content}
-                <span><button id="plupload-{$field}" class="btn btn-danger plupload" data-input-id="c-{$field}"{$filter}><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                <span><button type="button" id="plupload-{$field}" class="btn btn-danger plupload" data-input-id="c-{$field}"{$filter}{$multiple}{$preview}><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                <span><button type="button" id="fachoose-{$field}" class="btn btn-primary fachoose" data-input-id="c-{$field}"{$filter}{$multiple}><i class="fa fa-list"></i> {:__('Choose')}</button></span>
+                {$previewcontainer}
             </div>
 EOD;
     }
diff --git a/application/admin/command/Crud/stubs/javascript.stub b/application/admin/command/Crud/stubs/javascript.stub
index dde47a0..6b938cf 100644
--- a/application/admin/command/Crud/stubs/javascript.stub
+++ b/application/admin/command/Crud/stubs/javascript.stub
@@ -19,6 +19,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
             // 初始化表格
             table.bootstrapTable({
                 url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: '{%pk%}',
                 sortName: '{%order%}',
                 columns: [
                     [
diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php
index c009f88..6cd0a0a 100644
--- a/application/admin/controller/Index.php
+++ b/application/admin/controller/Index.php
@@ -70,18 +70,18 @@ class Index extends Backend
             $result = $validate->check($data);
             if (!$result)
             {
-                $this->error($validate->getError());
+                $this->error($validate->getError(), $url, ['token' => $this->request->token()]);
                 return;
             }
             $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0);
             if ($result === true)
             {
-                $this->success(__('Login successful'), $url);
+                $this->success(__('Login successful'), $url, ['url' => $url]);
                 return;
             }
             else
             {
-                $this->error(__('Username or password is incorrect'), $url);
+                $this->error(__('Username or password is incorrect'), $url, ['token' => $this->request->token()]);
             }
             return;
         }
diff --git a/application/admin/controller/general/Attachment.php b/application/admin/controller/general/Attachment.php
index 4d99303..ec43292 100644
--- a/application/admin/controller/general/Attachment.php
+++ b/application/admin/controller/general/Attachment.php
@@ -46,6 +46,32 @@ class Attachment extends Backend
         }
         return $this->view->fetch();
     }
+    
+    /**
+     * 选择附件
+     */
+    public function select()
+    {
+        if ($this->request->isAjax())
+        {
+            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
+            $total = $this->model
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->count();
+
+            $list = $this->model
+                    ->where($where)
+                    ->order($sort, $order)
+                    ->limit($offset, $limit)
+                    ->select();
+
+            $result = array("total" => $total, "rows" => $list);
+
+            return json($result);
+        }
+        return $this->view->fetch();
+    }
 
     /**
      * 添加
diff --git a/application/admin/library/traits/Backend.php b/application/admin/library/traits/Backend.php
index 51cd47c..301be41 100644
--- a/application/admin/library/traits/Backend.php
+++ b/application/admin/library/traits/Backend.php
@@ -2,7 +2,6 @@
 
 namespace app\admin\library\traits;
 
-
 trait Backend
 {
 
@@ -61,7 +60,7 @@ trait Backend
      */
     public function edit($ids = NULL)
     {
-        $row = $this->model->get(['id' => $ids]);
+        $row = $this->model->get($ids);
         if (!$row)
             $this->error(__('No Results were found'));
         if ($this->request->isPost())
@@ -93,7 +92,7 @@ trait Backend
         $this->code = -1;
         if ($ids)
         {
-            $count = $this->model->where('id', 'in', $ids)->delete();
+            $count = $this->model->destroy($ids);
             if ($count)
             {
                 $this->code = 1;
@@ -118,7 +117,7 @@ trait Backend
                 $values = array_intersect_key($values, array_flip(array('status')));
                 if ($values)
                 {
-                    $count = $this->model->where('id', 'in', $ids)->update($values);
+                    $count = $this->model->where($this->model->getPk(), 'in', $ids)->update($values);
                     if ($count)
                     {
                         $this->code = 1;
diff --git a/application/admin/view/common/header.html b/application/admin/view/common/header.html
index 369eb6a..eb3dee5 100644
--- a/application/admin/view/common/header.html
+++ b/application/admin/view/common/header.html
@@ -64,7 +64,7 @@
                 </a>
             </li>
 
-            <li>
+            <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':''}">
@@ -76,7 +76,7 @@
                 </ul>
             </li>
 
-            <li>
+            <li class="hidden-xs">
                 <a href="#" data-toggle="fullscreen"><i class="fa fa-arrows-alt"></i></a>
             </li>
 
diff --git a/application/admin/view/general/attachment/select.html b/application/admin/view/general/attachment/select.html
new file mode 100644
index 0000000..dc967fb
--- /dev/null
+++ b/application/admin/view/general/attachment/select.html
@@ -0,0 +1,22 @@
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="myTabContent" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    <div id="toolbar" class="toolbar">
+                        {:build_toolbar('refresh')}
+                        {if request()->get('multiple') == 'true'}
+                        <a class="btn btn-danger btn-choose-multi"><i class="fa fa-check"></i> {:__('Choose')}</a>
+                        {/if}
+                    </div>
+                    <table id="table" class="table table-bordered table-hover" width="100%">
+
+                    </table>
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>
diff --git a/application/admin/view/general/profile/index.html b/application/admin/view/general/profile/index.html
index 7ff0d7d..4a0d6a3 100644
--- a/application/admin/view/general/profile/index.html
+++ b/application/admin/view/general/profile/index.html
@@ -41,7 +41,7 @@
                     <div class="box-body box-profile">
                         
                         <div class="profile-avatar-container">
-                            <img class="profile-user-img img-responsive img-circle plupload" src="{$admin.avatar}" alt="">
+                            <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>
                         </div>
diff --git a/application/admin/view/page/add.html b/application/admin/view/page/add.html
index 2ba52a7..cf979c6 100644
--- a/application/admin/view/page/add.html
+++ b/application/admin/view/page/add.html
@@ -28,7 +28,8 @@
         <div class="col-xs-12 col-sm-8">
             <div class="form-inline">
                 <input id="c-image" class="form-control" size="50" name="row[image]" type="text" value="">
-                <span><button id="plupload-image" class="btn btn-danger plupload" data-input-id="c-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                <span><button id="plupload-image" class="btn btn-danger plupload" data-input-id="c-image" data-preview-id="plupload-preview-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                <ul class="row list-inline plupload-preview" id="plupload-preview-image"></ul>
             </div>
         </div>
     </div>
diff --git a/application/admin/view/page/edit.html b/application/admin/view/page/edit.html
index 0bf29d6..c44deea 100644
--- a/application/admin/view/page/edit.html
+++ b/application/admin/view/page/edit.html
@@ -29,7 +29,9 @@
         <div class="col-xs-12 col-sm-8">
             <div class="form-inline">
                 <input id="c-image" class="form-control" size="50" name="row[image]" type="text" value="{$row.image}">
-                <span><button id="plupload-image" class="btn btn-danger plupload" data-input-id="c-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                <span><button type="button" id="plupload-image" class="btn btn-danger plupload" data-multiple="false" data-input-id="c-image" data-preview-id="p-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
+                <span><button type="button" id="fachoose-image" class="btn btn-primary fachoose" data-multiple="false" data-input-id="c-image"><i class="fa fa-list-ul"></i> {:__('Choose')}</button></span>
+                <ul class="row list-inline plupload-preview" id="p-image"></ul>
             </div>
         </div>
     </div>
diff --git a/application/common/controller/Backend.php b/application/common/controller/Backend.php
index 36ac9f2..afbd2bf 100644
--- a/application/common/controller/Backend.php
+++ b/application/common/controller/Backend.php
@@ -93,19 +93,22 @@ class Backend extends Controller
 
         // 设置当前请求的URI
         $this->auth->setRequestUri($path);
-
         // 检测是否需要验证登录
         if (!$this->auth->match($this->noNeedLogin))
         {
             //检测是否登录
             if (!$this->auth->isLogin())
             {
-                $this->error(__('Please login first'), url('index/login', ['url' => $this->request->url()]));
+                $url = Session::get('referer');
+                $url = $url ? $url : $this->request->url();
+                $this->error(__('Please login first'), url('index/login', ['url' => $url]));
             }
             // 判断是否需要验证权限
             if (!$this->auth->match($this->noNeedRight))
             {
                 // 判断控制器和方法判断是否有对应权限
+                $path = $this->request->path();
+                $path = substr($path, 0, 1) == '/' ? $path : '/' . $path;
                 if (!$this->auth->check($path))
                 {
                     $this->error(__('You have no permission'), NULL);
diff --git a/application/uc.php b/application/uc.php
index e987868..17976ba 100644
--- a/application/uc.php
+++ b/application/uc.php
@@ -2,7 +2,7 @@
 
 //UC配置
 // Ucenter配置配置
-define('UC_STATUS', TRUE);  //是否开启Ucenter同步
+define('UC_STATUS', false);  //是否开启Ucenter同步
 
 define('UC_CONNECT', 'mysql');
 
diff --git a/bower.json b/bower.json
index 8de48d2..afe4be9 100644
--- a/bower.json
+++ b/bower.json
@@ -26,7 +26,7 @@
     "bootstrap3-dialog": "bootstrap-dialog#^1.35.3",
     "require-css": "^0.1.8",
     "less": "^2.7.1",
-    "tableExport.jquery.plugin": "^1.6.4",
+    "tableExport.jquery.plugin": "^1.9.0",
     "jquery-slimscroll": "slimscroll#^1.3.8",
     "jquery.cookie": "^1.4.1",
     "Sortable": "^1.5.0",
diff --git a/extend/fast/Auth.php b/extend/fast/Auth.php
index 07a0d3f..86473eb 100644
--- a/extend/fast/Auth.php
+++ b/extend/fast/Auth.php
@@ -209,7 +209,7 @@ class Auth
             $_rulelist[$uid] = [];
             return [];
         }
-        
+
         // 筛选条件
         $where = [
             'status' => 'normal'
@@ -220,9 +220,13 @@ class Auth
         }
         //读取用户组所有权限规则
         $this->rules = Db::name($this->config['auth_rule'])->where($where)->field('id,pid,condition,icon,name,title,ismenu')->select();
-        
+
         //循环规则,判断结果。
         $rulelist = []; //
+        if (in_array('*', $ids))
+        {
+            $rulelist[] = "*";
+        }
         foreach ($this->rules as $rule)
         {
             //超级管理员无需验证condition
diff --git a/public/assets/css/backend.css b/public/assets/css/backend.css
index 7133742..915fc98 100644
--- a/public/assets/css/backend.css
+++ b/public/assets/css/backend.css
@@ -106,8 +106,9 @@ body {
   font-weight: normal;
 }
 .user-panel > .image img {
-  width: 45px;
-  height: 45px;
+  width: 100%;
+  max-width: 45px;
+  max-height: 45px;
 }
 /*panel扩展描述样式*/
 .panel-intro {
@@ -329,6 +330,26 @@ body {
 .note-editor .note-editing-area .note-editable {
   display: block !important;
 }
+.plupload-preview {
+  padding: 10px;
+  margin-bottom: 0;
+}
+.plupload-preview li {
+  margin-bottom: 10px;
+}
+.plupload-preview .thumbnail {
+  margin-bottom: 10px;
+}
+.plupload-preview a {
+  display: block;
+}
+.plupload-preview a:first-child {
+  height: 90px;
+}
+.plupload-preview a img {
+  height: 80px;
+  object-fit: cover;
+}
 .pjax-loader-bar .progress {
   position: fixed;
   top: 0;
@@ -526,6 +547,9 @@ body {
   .nav-addtabs {
     display: none;
   }
+  .fixed-table-toolbar .columns-right.btn-group {
+    display: none;
+  }
 }
 /*平板样式*/
 /*# sourceMappingURL=backend.css.map */
\ No newline at end of file
diff --git a/public/assets/js/backend/general/attachment.js b/public/assets/js/backend/general/attachment.js
index 2e683f8..a68b0f6 100644
--- a/public/assets/js/backend/general/attachment.js
+++ b/public/assets/js/backend/general/attachment.js
@@ -42,6 +42,63 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
             Table.api.bindevent(table);
 
         },
+        select: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'general/attachment/select',
+                }
+            });
+
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                sortName: 'id',
+                columns: [
+                    [
+                        {field: 'state', checkbox: true, },
+                        {field: 'id', title: __('Id')},
+                        {field: 'url', title: __('Preview'), formatter: Controller.api.formatter.thumb},
+                        {field: 'imagewidth', title: __('Imagewidth')},
+                        {field: 'imageheight', title: __('Imageheight')},
+                        {field: 'mimetype', title: __('Mimetype'), operate: 'LIKE %...%',
+                            process: function (value, arg) {
+                                return value.replace(/\*/g, '%');
+                            }},
+                        {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime},
+                        {field: 'operate', title: __('Operate'), events: {
+                                'click .btn-chooseone': function (e, value, row, index) {
+                                    var callback = Backend.api.query('callback');
+                                    var id = Backend.api.query('element_id');
+                                    var multiple = Backend.api.query('multiple');
+                                    multiple = multiple == 'true' ? true : false;
+                                    if (id && callback) {
+                                        parent.window[callback](id, {url: row.url}, multiple);
+                                    }
+                                },
+                            }, formatter: function () {
+                                return '<a href="javascript:;" class="btn btn-danger btn-chooseone btn-xs"><i class="fa fa-check"></i> ' + __('Choose') + '</a>';
+                            }}
+                    ]
+                ]
+            });
+
+            // 选中多个
+            $(document).on("click", ".btn-choose-multi", function () {
+                var callback = Backend.api.query('callback');
+                var id = Backend.api.query('element_id');
+                var urlArr = new Array();
+                $.each(table.bootstrapTable("getAllSelections"), function (i, j) {
+                    urlArr.push(j.url);
+                });
+                parent.window[callback](id, {url: urlArr.join(",")}, true);
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
         add: function () {
             Controller.api.bindevent();
         },
diff --git a/public/assets/js/backend/index.js b/public/assets/js/backend/index.js
index 67ffc50..b3318bf 100755
--- a/public/assets/js/backend/index.js
+++ b/public/assets/js/backend/index.js
@@ -141,7 +141,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
             if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
                 $(".tab-addtabs").addClass("ios-iframe-fix");
             }
-            
+
             if (Config.referer) {
                 //刷新页面后跳到到刷新前的页面
                 Backend.api.addtabs(Config.referer);
@@ -322,8 +322,8 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
             $(window).resize();
         },
         login: function () {
-            Form.api.bindevent($("#login-form"), null, function () {
-                location.href = Backend.api.fixurl("index/index");
+            Form.api.bindevent($("#login-form"), null, function (data) {
+                location.href = Backend.api.fixurl(data.url);
             });
         }
     };
diff --git a/public/assets/js/require-form.js b/public/assets/js/require-form.js
index 1bde208..a5b88c1 100755
--- a/public/assets/js/require-form.js
+++ b/public/assets/js/require-form.js
@@ -51,6 +51,9 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'upload', 'validator'], func
                                 }
                                 Toastr.success(msg ? msg : __('Operation completed'));
                             } else {
+                                if (typeof data.token !== 'undefined') {
+                                    $("input[name='__token__']").val(data.token);
+                                }
                                 Toastr.error(msg ? msg : __('Operation failed'));
                             }
                         } else {
@@ -230,6 +233,33 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'upload', 'validator'], func
                 if ($(".plupload", form).size() > 0) {
                     Upload.api.plupload();
                 }
+
+                //绑定fachoose选择附件事件
+                if ($(".fachoose", form).size() > 0) {
+                    $(document).on('click', ".fachoose", function () {
+                        var multiple = $(this).data("multiple") ? $(this).data("multiple") : false;
+                        var mimetype = $(this).data("mimetype") ? $(this).data("mimetype") : '';
+                        Backend.api.open("general/attachment/select?callback=refreshchoose&element_id=" + $(this).attr("id") + "&multiple=" + multiple + "&mimetype="+mimetype, __('Choose'));
+                        return false;
+                    });
+
+                    //刷新选择的元素
+                    window.refreshchoose = function (id, data, multiple) {
+                        var input_id = $("#" + id).data("input-id");
+                        if (multiple) {
+                            var urlArr = [];
+                            var inputObj = $("#" + input_id);
+                            if (inputObj.val() != "") {
+                                urlArr.push(inputObj.val());
+                            }
+                            urlArr.push(data.url);
+                            inputObj.val(urlArr.join(",")).trigger("change");
+                        } else {
+                            $("#" + input_id).val(data.url).trigger("change");
+                        }
+                        layer.closeAll();
+                    };
+                }
             },
             custom: {}
         },
diff --git a/public/assets/js/require-table.js b/public/assets/js/require-table.js
index cccafda..88b86ea 100644
--- a/public/assets/js/require-table.js
+++ b/public/assets/js/require-table.js
@@ -24,6 +24,7 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
             locale: 'zh-CN',
             showToggle: true,
             showColumns: true,
+            pk: 'id',
             sortName: 'id',
             sortOrder: 'desc',
             paginationFirstText: __("First"),
@@ -110,6 +111,7 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', '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);
 
                     // 挺拽选择,需要重新绑定事件
                     require(['drag', 'drop'], function () {
@@ -187,24 +189,25 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
                         dragEnd: function () {
                             var data = table.bootstrapTable('getData');
                             var current = data[parseInt($(this).data("index"))];
+                            var options = table.bootstrapTable('getOptions');
                             //改变的值和改变的ID集合
                             var ids = $.map($("tbody tr:visible", table), function (tr) {
-                                return data[parseInt($(tr).data("index"))].id;
+                                return data[parseInt($(tr).data("index"))][options.pk];
                             });
-                            var changeid = current.id;
+                            var changeid = current[options.pk];
                             var pid = typeof current.pid != 'undefined' ? current.pid : '';
-                            var options = {
+                            var params = {
                                 url: table.bootstrapTable('getOptions').extend.dragsort_url,
                                 data: {
                                     ids: ids.join(','),
                                     changeid: changeid,
                                     pid: pid,
                                     field: Table.config.dragsortfield,
-                                    orderway: table.bootstrapTable('getOptions').sortOrder,
-                                    table: table.bootstrapTable('getOptions').extend.table
+                                    orderway: options.sortOrder,
+                                    table: options.extend.table
                                 }
                             };
-                            Backend.api.ajax(options, function (data) {
+                            Backend.api.ajax(params, function (data) {
                                 Toastr.success(__('Operation completed'));
                                 table.bootstrapTable('refresh');
                             });
@@ -233,7 +236,7 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
                 operate: {
                     'click .btn-editone': function (e, value, row, index) {
                         var options = $(this).closest('table').bootstrapTable('getOptions');
-                        Backend.api.open(options.extend.edit_url + "/ids/" + row.id, __('Edit'));
+                        Backend.api.open(options.extend.edit_url + "/ids/" + row[options.pk], __('Edit'));
                     },
                     'click .btn-delone': function (e, value, row, index) {
                         var that = this;
@@ -250,7 +253,8 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
                                 {icon: 3, title: __('Warning'), offset: [top, left], shadeClose: true},
                                 function () {
                                     var table = $(that).closest('table');
-                                    Table.api.multi("del", row.id, table, that);
+                                    var options = table.bootstrapTable('getOptions');
+                                    Table.api.multi("del", row[options.pk], table, that);
                                     Backend.api.layer.close(index);
                                 }
                         );
@@ -341,8 +345,9 @@ define(['jquery', 'bootstrap', 'backend', 'toastr', 'moment', 'bootstrap-table',
             },
             // 获取选中的条目ID集合
             selectedids: function (table) {
+                var options = table.bootstrapTable('getOptions');
                 return $.map(table.bootstrapTable('getSelections'), function (row) {
-                    return row.id
+                    return row[options.pk];
                 });
             },
             // 切换复选框状态
diff --git a/public/assets/js/require-upload.js b/public/assets/js/require-upload.js
index b1ee69f..943ffac 100755
--- a/public/assets/js/require-upload.js
+++ b/public/assets/js/require-upload.js
@@ -1,15 +1,17 @@
-define(['jquery', 'bootstrap', 'backend', 'plupload'], function ($, undefined, Backend, Plupload) {
+define(['jquery', 'bootstrap', 'backend', 'plupload', 'dragsort', 'template'], function ($, undefined, Backend, Plupload, Dragsort, Template) {
     var Upload = {
         list: {},
         config: {
             container: document.body,
-            classname: '.plupload',
+            classname: '.plupload:not([initialized])',
+            previewtpl: '<li class="col-xs-3"><a href="<%=fullurl%>" data-url="<%=url%>" target="_blank" class="thumbnail"><img src="<%=fullurl%>" class="img-responsive"></a><a href="javascript:;" class="btn btn-danger btn-xs btn-trash"><i class="fa fa-trash"></i></a></li>',
         },
         api: {
             //Plupload上传
             plupload: function (element, onAfterUpload) {
                 element = typeof element == 'undefined' ? Upload.config.classname : element;
                 $(element, Upload.config.container).each(function () {
+                    $(this).attr("initialized", true);
                     var that = this;
                     var id = $(this).prop("id");
                     var url = $(this).data("url");
@@ -20,14 +22,18 @@ define(['jquery', 'bootstrap', 'backend', 'plupload'], function ($, undefined, B
                     //上传URL
                     url = url ? url : Config.upload.uploadurl;
                     url = Backend.api.fixurl(url);
+                    //填充ID
+                    var input_id = $(that).data("input-id") ? $(that).data("input-id") : "";
+                    //预览ID
+                    var preview_id = $(that).data("preview-id") ? $(that).data("preview-id") : "";
                     //最大可上传
-                    maxsize = maxsize ? maxsize : Config.upload.maxsize;
+                    maxsize = typeof maxsize !== "undefined" ? maxsize : Config.upload.maxsize;
                     //文件类型
-                    mimetype = mimetype ? mimetype : Config.upload.mimetype;
+                    mimetype = typeof mimetype !== "undefined" ? mimetype : Config.upload.mimetype;
                     //请求的表单参数
-                    multipart = multipart ? multipart : Config.upload.multipart;
+                    multipart = typeof multipart !== "undefined" ? multipart : Config.upload.multipart;
                     //是否支持批量上传
-                    multiple = multiple ? multiple : Config.upload.multiple;
+                    multiple = typeof multiple !== "undefined" ? multiple : Config.upload.multiple;
                     //生成Plupload实例
                     Upload.list[id] = new Plupload.Uploader({
                         runtimes: 'html5,flash,silverlight,html4',
@@ -63,6 +69,7 @@ define(['jquery', 'bootstrap', 'backend', 'plupload'], function ($, undefined, B
                                 $(that).prop("disabled", true).html("<i class='fa fa-upload'></i> 上传" + file.percent + "%");
                             },
                             FileUploaded: function (up, file, info) {
+                                var options = this.getOption();
                                 //还原按钮文字及状态
                                 $(that).prop("disabled", false).html($(that).data("bakup-html"));
                                 //这里可以改成其它的表现形式
@@ -76,9 +83,14 @@ define(['jquery', 'bootstrap', 'backend', 'plupload'], function ($, undefined, B
                                         var data = ret.hasOwnProperty("data") && ret.data != "" ? ret.data : null;
                                         var msg = ret.hasOwnProperty("msg") && ret.msg != "" ? ret.msg : "";
                                         if (ret.code === 1) {
-                                            //$("input[data-plupload-id='" + id + "-text']").val(data.url);
-                                            if ($(that).data("input-id")) {
-                                                $("input#" + $(that).data("input-id")).val(data.url);
+                                            if (input_id) {
+                                                var urlArr = [];
+                                                var inputObj = $("#" + input_id);
+                                                if (options.multi_selection && inputObj.val() != "") {
+                                                    urlArr.push(inputObj.val());
+                                                }
+                                                urlArr.push(data.url);
+                                                inputObj.val(urlArr.join(",")).trigger("change");
                                             }
                                             var afterUpload = $("#" + id).data("after-upload");
                                             if (afterUpload && typeof Upload.api.custom[afterUpload] == 'function') {
@@ -102,6 +114,49 @@ define(['jquery', 'bootstrap', 'backend', 'plupload'], function ($, undefined, B
                             }
                         }
                     });
+
+                    //拖动排序
+                    if (preview_id && multiple) {
+                        $("#" + preview_id).dragsort({
+                            dragSelector: "li",
+                            dragEnd: function () {
+                                $("#" + preview_id).trigger("fa.preview.change");
+                            },
+                            placeHolderTemplate: '<li class="col-xs-3"></li>'
+                        });
+                    }
+                    if (preview_id && input_id) {
+                        $(document.body).on("keyup change", "#" + input_id, function () {
+                            var inputStr = $("#" + input_id).val();
+                            var inputArr = inputStr.split(/\,/);
+                            $("#" + preview_id).empty();
+                            $.each(inputArr, function (i, j) {
+                                if (!j) {
+                                    return true;
+                                }
+                                var html = Template.render(Upload.config.previewtpl, {url: j, fullurl: Config.upload.cdnurl + j});
+                                $("#" + preview_id).append(html);
+                            });
+                        });
+                        $("#" + input_id).trigger("keyup");
+                    }
+                    if (preview_id) {
+                        // 监听事件
+                        $(document.body).on("fa.preview.change", "#" + preview_id, function () {
+                            var urlArr = new Array();
+                            $("#" + preview_id + " [data-url]").each(function (i, j) {
+                                urlArr.push($(this).data("url"));
+                            });
+                            if (input_id) {
+                                $("#" + input_id).val(urlArr.join(","));
+                            }
+                        });
+                        //移除按钮事件
+                        $(document.body).on("click", "#" + preview_id + " .btn-trash", function () {
+                            $(this).closest("li").remove();
+                            $("#" + preview_id).trigger("fa.preview.change");
+                        });
+                    }
                     Upload.list[id].init();
                 });
             },
diff --git a/public/assets/less/backend.less b/public/assets/less/backend.less
index 39ab80a..7fcb21a 100644
--- a/public/assets/less/backend.less
+++ b/public/assets/less/backend.less
@@ -140,8 +140,9 @@ body {
 }
 
 .user-panel > .image img{
-    width:45px;
-    height:45px;
+    width:100%;
+    max-width:45px;
+    max-height:45px;
 }
 
 /*panel扩展描述样式*/
@@ -364,6 +365,27 @@ body {
     display: block !important;
 }
 
+.plupload-preview {
+    padding:10px;
+    margin-bottom:0;
+    li {
+        margin-bottom:10px;
+    }
+    .thumbnail {
+        margin-bottom:10px;
+    }
+    a{
+        display:block;
+        &:first-child{
+            height:90px;
+        }
+        img{
+            height:80px;
+            object-fit: cover;
+        }
+    }
+}
+
 .pjax-loader-bar .progress {
     position: fixed;
     top: 0;
@@ -567,6 +589,11 @@ body {
     .nav-addtabs {
         display:none;
     }
+    .fixed-table-toolbar {
+        .columns-right.btn-group{
+            display:none;
+        }
+    }
 }
 /*平板样式*/
 @media (max-width: @screen-tablet) {
--
libgit2 0.24.0