Browse Source

修复searchList使用二维数组数据源时排序丢失的BUG
修复后台插件管理登录状态失效的处理和判断
优化列表页通用搜索样式和排版调整
修复关联搜索使用find_in_set时的BUG
优化CRUD在生成中文数据时的编码问题

Karson 7 years ago
parent
commit
045647c407

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

@@ -1215,7 +1215,7 @@ EOD;
                 $v = "__('" . $v . "')";
                 $v = "__('" . $v . "')";
         }
         }
         unset($v);
         unset($v);
-        $searchList = json_encode($itemArr, JSON_FORCE_OBJECT);
+        $searchList = json_encode($itemArr, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE);
         $searchList = str_replace(['":"', '"}', ')","'], ['":', '}', '),"'], $searchList);
         $searchList = str_replace(['":"', '"}', ')","'], ['":', '}', '),"'], $searchList);
         if ($itemArr && !$extend) {
         if ($itemArr && !$extend) {
             $html .= ", searchList: " . $searchList;
             $html .= ", searchList: " . $searchList;

+ 5 - 2
application/admin/lang/zh-cn/addon.php

@@ -21,13 +21,14 @@ return [
     'Pay tips'                       => '扫码支付后如果仍然无法立即下载,请不要重复支付,请加<a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank">QQ群:636393962</a>向管理员反馈',
     'Pay tips'                       => '扫码支付后如果仍然无法立即下载,请不要重复支付,请加<a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank">QQ群:636393962</a>向管理员反馈',
     'Pay click tips'                 => '请点击这里在新窗口中进行支付!',
     'Pay click tips'                 => '请点击这里在新窗口中进行支付!',
     'Pay new window tips'            => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!',
     'Pay new window tips'            => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!',
-    'Uninstall tips'                 => '确认卸载插件?<p class="text-danger">卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!</p>如有重要数据请备份后再操作!',
-    'Upgrade tips'                   => '确认升级插件?<p class="text-danger">如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!<br>升级后可能出现部分冗余数据记录,请根据需要移除即可!!!</p>如有重要数据请备份后再操作!',
+    'Uninstall tips'                 => '确认卸载[%s]?<p class="text-danger">卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!</p>如有重要数据请备份后再操作!',
+    'Upgrade tips'                   => '确认升级[%s]?<p class="text-danger">如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!<br>升级后可能出现部分冗余数据记录,请根据需要移除即可!!!</p>如有重要数据请备份后再操作!',
     'Offline installed tips'         => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
     'Offline installed tips'         => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
     'Online installed tips'          => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
     'Online installed tips'          => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
     'Not login tips'                 => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!',
     'Not login tips'                 => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!',
     'Not installed tips'             => '请安装后再访问插件前台页面!',
     'Not installed tips'             => '请安装后再访问插件前台页面!',
     'Not enabled tips'               => '插件已经禁用,请启用后再访问插件前台页面!',
     'Not enabled tips'               => '插件已经禁用,请启用后再访问插件前台页面!',
+    'New version tips'               => '发现新版本:%s 点击查看更新日志',
     'Please disable addon first'     => '请先禁用插件再进行升级',
     'Please disable addon first'     => '请先禁用插件再进行升级',
     'Login now'                      => '立即登录',
     'Login now'                      => '立即登录',
     'Continue install'               => '不登录,继续安装',
     'Continue install'               => '不登录,继续安装',
@@ -36,6 +37,8 @@ return [
     'View addon screenshots'         => '点击查看插件截图',
     'View addon screenshots'         => '点击查看插件截图',
     'Click to toggle status'         => '点击切换插件状态',
     'Click to toggle status'         => '点击切换插件状态',
     'Click to contact developer'     => '点击与插件开发者取得联系',
     'Click to contact developer'     => '点击与插件开发者取得联系',
+    'My addons'                      => '我购买的插件',
+    'My posts'                       => '我发布的插件',
     'Index'                          => '前台',
     'Index'                          => '前台',
     'All'                            => '全部',
     'All'                            => '全部',
     'Uncategoried'                   => '未归类',
     'Uncategoried'                   => '未归类',

+ 19 - 10
application/admin/view/addon/index.html

@@ -53,6 +53,9 @@
         position: absolute;
         position: absolute;
         box-shadow: 0px 0px 2px #f11414;
         box-shadow: 0px 0px 2px #f11414;
     }
     }
+    .form-userinfo .breadcrumb {
+        margin-bottom:10px;
+    }
 </style>
 </style>
 <div class="panel panel-default panel-intro">
 <div class="panel panel-default panel-intro">
     <div class="panel-heading">
     <div class="panel-heading">
@@ -158,17 +161,21 @@
                     <strong>{:__('Warning')}</strong><br/>{:__('Login tips')}
                     <strong>{:__('Warning')}</strong><br/>{:__('Login tips')}
                 </div>
                 </div>
                 <div class="form-group">
                 <div class="form-group">
-                    <label for="inputAccount" class="col-lg-3 control-label">{:__('Username')}</label>
-                    <div class="col-lg-9">
-                        <input type="text" class="form-control" id="inputAccount" value=""
-                               placeholder="{:__('Your username or email')}">
+                    <div class="col-lg-12">
+                        <div class="input-group">
+                            <span class="input-group-addon"><i class="fa fa-user"></i></span>
+                            <input type="text" class="form-control" id="inputAccount" value=""
+                                   placeholder="{:__('Your username or email')}">
+                        </div>
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="form-group">
                 <div class="form-group">
-                    <label for="inputPassword" class="col-lg-3 control-label">{:__('Password')}</label>
-                    <div class="col-lg-9">
+                    <div class="col-lg-12">
+                        <div class="input-group">
+                            <span class="input-group-addon"><i class="fa fa-lock"></i></span>
                         <input type="password" class="form-control" id="inputPassword" value=""
                         <input type="password" class="form-control" id="inputPassword" value=""
                                placeholder="{:__('Your password')}">
                                placeholder="{:__('Your password')}">
+                        </div>
                     </div>
                     </div>
                 </div>
                 </div>
             </fieldset>
             </fieldset>
@@ -177,13 +184,15 @@
 </script>
 </script>
 <script id="userinfotpl" type="text/html">
 <script id="userinfotpl" type="text/html">
     <div>
     <div>
-        <form class="form-horizontal">
+        <form class="form-horizontal form-userinfo">
             <fieldset>
             <fieldset>
                 <div class="alert alert-dismissable alert-success">
                 <div class="alert alert-dismissable alert-success">
                     <button type="button" class="close" data-dismiss="alert">×</button>
                     <button type="button" class="close" data-dismiss="alert">×</button>
                     <strong>{:__('Warning')}</strong><br/>{:__('Logined tips', '<%=username%>')}
                     <strong>{:__('Warning')}</strong><br/>{:__('Logined tips', '<%=username%>')}
                 </div>
                 </div>
             </fieldset>
             </fieldset>
+            <div class="breadcrumb"><a href="https://www.fastadmin.net/user/myaddon.html" target="_blank"><i class="fa fa-money"></i> {:__('My addons')}</a></div>
+            <div class="breadcrumb"><a href="https://www.fastadmin.net/user/addon.html" target="_blank"><i class="fa fa-upload"></i> {:__('My posts')}</a></div>
         </form>
         </form>
     </div>
     </div>
 </script>
 </script>
@@ -273,15 +282,15 @@
                                 </ul>
                                 </ul>
                             </span>
                             </span>
         <% }else{%>
         <% }else{%>
-        <a href="javascript:;" class="btn btn-xs btn-info btn-upgrade" data-version="<%=item.version%>"><i
+        <a href="javascript:;" class="btn btn-xs btn-info btn-upgrade" title="{:__('Upgrade')}" data-version="<%=item.version%>"><i
                 class="fa fa-cloud"></i> {:__('Upgrade')}</a>
                 class="fa fa-cloud"></i> {:__('Upgrade')}</a>
         <% }%>
         <% }%>
         <% }%>
         <% }%>
         <% if(addon.config){ %>
         <% if(addon.config){ %>
-        <a href="javascript:;" class="btn btn-xs btn-primary btn-config"><i class="fa fa-pencil"></i>
+        <a href="javascript:;" class="btn btn-xs btn-primary btn-config" title="{:__('Setting')}"><i class="fa fa-pencil"></i>
             {:__('Setting')}</a>
             {:__('Setting')}</a>
         <% } %>
         <% } %>
-        <a href="javascript:;" class="btn btn-xs btn-danger btn-uninstall"><i class="fa fa-times"></i>
+        <a href="javascript:;" class="btn btn-xs btn-danger btn-uninstall" title="{:__('Uninstall')}"><i class="fa fa-times"></i>
             {:__('Uninstall')}</a>
             {:__('Uninstall')}</a>
         <% } %>
         <% } %>
 
 

+ 71 - 98
application/common/controller/Backend.php

@@ -40,6 +40,12 @@ class Backend extends Controller
     protected $auth = null;
     protected $auth = null;
 
 
     /**
     /**
+     * 模型对象
+     * @var \think\Model
+     */
+    protected $model = null;
+
+    /**
      * 快速搜索时执行查找的字段
      * 快速搜索时执行查找的字段
      */
      */
     protected $searchFields = 'id';
     protected $searchFields = 'id';
@@ -52,7 +58,7 @@ class Backend extends Controller
     /**
     /**
      * 是否开启数据限制
      * 是否开启数据限制
      * 支持auth/personal
      * 支持auth/personal
-     * 表示按权限判断/仅限个人 
+     * 表示按权限判断/仅限个人
      * 默认为禁用,若启用请务必保证表中存在admin_id字段
      * 默认为禁用,若启用请务必保证表中存在admin_id字段
      */
      */
     protected $dataLimit = false;
     protected $dataLimit = false;
@@ -116,22 +122,18 @@ class Backend extends Controller
         // 设置当前请求的URI
         // 设置当前请求的URI
         $this->auth->setRequestUri($path);
         $this->auth->setRequestUri($path);
         // 检测是否需要验证登录
         // 检测是否需要验证登录
-        if (!$this->auth->match($this->noNeedLogin))
-        {
+        if (!$this->auth->match($this->noNeedLogin)) {
             //检测是否登录
             //检测是否登录
-            if (!$this->auth->isLogin())
-            {
+            if (!$this->auth->isLogin()) {
                 Hook::listen('admin_nologin', $this);
                 Hook::listen('admin_nologin', $this);
                 $url = Session::get('referer');
                 $url = Session::get('referer');
                 $url = $url ? $url : $this->request->url();
                 $url = $url ? $url : $this->request->url();
                 $this->error(__('Please login first'), url('index/login', ['url' => $url]));
                 $this->error(__('Please login first'), url('index/login', ['url' => $url]));
             }
             }
             // 判断是否需要验证权限
             // 判断是否需要验证权限
-            if (!$this->auth->match($this->noNeedRight))
-            {
+            if (!$this->auth->match($this->noNeedRight)) {
                 // 判断控制器和方法判断是否有对应权限
                 // 判断控制器和方法判断是否有对应权限
-                if (!$this->auth->check($path))
-                {
+                if (!$this->auth->check($path)) {
                     Hook::listen('admin_nopermission', $this);
                     Hook::listen('admin_nopermission', $this);
                     $this->error(__('You have no permission'), '');
                     $this->error(__('You have no permission'), '');
                 }
                 }
@@ -139,15 +141,12 @@ class Backend extends Controller
         }
         }
 
 
         // 非选项卡时重定向
         // 非选项卡时重定向
-        if (!$this->request->isPost() && !IS_AJAX && !IS_ADDTABS && !IS_DIALOG && input("ref") == 'addtabs')
-        {
-            $url = preg_replace_callback("/([\?|&]+)ref=addtabs(&?)/i", function($matches) {
+        if (!$this->request->isPost() && !IS_AJAX && !IS_ADDTABS && !IS_DIALOG && input("ref") == 'addtabs') {
+            $url = preg_replace_callback("/([\?|&]+)ref=addtabs(&?)/i", function ($matches) {
                 return $matches[2] == '&' ? $matches[1] : '';
                 return $matches[2] == '&' ? $matches[1] : '';
             }, $this->request->url());
             }, $this->request->url());
-            if (Config::get('url_domain_deploy'))
-            {
-                if (stripos($url, $this->request->server('SCRIPT_NAME')) === 0)
-                {
+            if (Config::get('url_domain_deploy')) {
+                if (stripos($url, $this->request->server('SCRIPT_NAME')) === 0) {
                     $url = substr($url, strlen($this->request->server('SCRIPT_NAME')));
                     $url = substr($url, strlen($this->request->server('SCRIPT_NAME')));
                 }
                 }
                 $url = url($url, '', false);
                 $url = url($url, '', false);
@@ -162,8 +161,7 @@ class Backend extends Controller
         $this->view->breadcrumb = $breadcrumb;
         $this->view->breadcrumb = $breadcrumb;
 
 
         // 如果有使用模板布局
         // 如果有使用模板布局
-        if ($this->layout)
-        {
+        if ($this->layout) {
             $this->view->engine->layout('layout/' . $this->layout);
             $this->view->engine->layout('layout/' . $this->layout);
         }
         }
 
 
@@ -220,7 +218,7 @@ class Backend extends Controller
     /**
     /**
      * 渲染配置信息
      * 渲染配置信息
      * @param mixed $name 键名或数组
      * @param mixed $name 键名或数组
-     * @param mixed $value 值 
+     * @param mixed $value 值
      */
      */
     protected function assignconfig($name, $value = '')
     protected function assignconfig($name, $value = '')
     {
     {
@@ -244,48 +242,46 @@ class Backend extends Controller
         $order = $this->request->get("order", "DESC");
         $order = $this->request->get("order", "DESC");
         $offset = $this->request->get("offset", 0);
         $offset = $this->request->get("offset", 0);
         $limit = $this->request->get("limit", 0);
         $limit = $this->request->get("limit", 0);
-        $filter = json_decode($filter, TRUE);
-        $op = json_decode($op, TRUE);
+        $filter = (array)json_decode($filter, TRUE);
+        $op = (array)json_decode($op, TRUE);
         $filter = $filter ? $filter : [];
         $filter = $filter ? $filter : [];
         $where = [];
         $where = [];
         $tableName = '';
         $tableName = '';
-        if ($relationSearch)
-        {
-            if (!empty($this->model))
-            {
-                $tableName = $this->model->getQuery()->getTable() . ".";
+        if ($relationSearch) {
+            if (!empty($this->model)) {
+                $name = \think\Loader::parseName(basename(str_replace('\\', '/', get_class($this->model))));
+                $tableName = $name . '.';
+            }
+            $sortArr = explode(',', $sort);
+            foreach ($sortArr as $index => & $item) {
+                $item = stripos($item, ".") === false ? $tableName . trim($item) : $item;
             }
             }
-            $sort = stripos($sort, ".") === false ? $tableName . $sort : $sort;
+            unset($item);
+            $sort = implode(',', $sortArr);
         }
         }
         $adminIds = $this->getDataLimitAdminIds();
         $adminIds = $this->getDataLimitAdminIds();
-        if (is_array($adminIds))
-        {
+        if (is_array($adminIds)) {
             $where[] = [$tableName . $this->dataLimitField, 'in', $adminIds];
             $where[] = [$tableName . $this->dataLimitField, 'in', $adminIds];
         }
         }
-        if ($search)
-        {
+        if ($search) {
             $searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
             $searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
-            foreach ($searcharr as $k => &$v)
-            {
+            foreach ($searcharr as $k => &$v) {
                 $v = stripos($v, ".") === false ? $tableName . $v : $v;
                 $v = stripos($v, ".") === false ? $tableName . $v : $v;
             }
             }
             unset($v);
             unset($v);
             $where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
             $where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
         }
         }
-        foreach ($filter as $k => $v)
-        {
+        foreach ($filter as $k => $v) {
             $sym = isset($op[$k]) ? $op[$k] : '=';
             $sym = isset($op[$k]) ? $op[$k] : '=';
-            if (stripos($k, ".") === false)
-            {
+            if (stripos($k, ".") === false) {
                 $k = $tableName . $k;
                 $k = $tableName . $k;
             }
             }
             $v = !is_array($v) ? trim($v) : $v;
             $v = !is_array($v) ? trim($v) : $v;
             $sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
             $sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
-            switch ($sym)
-            {
+            switch ($sym) {
                 case '=':
                 case '=':
                 case '!=':
                 case '!=':
-                    $where[] = [$k, $sym, (string) $v];
+                    $where[] = [$k, $sym, (string)$v];
                     break;
                     break;
                 case 'LIKE':
                 case 'LIKE':
                 case 'NOT LIKE':
                 case 'NOT LIKE':
@@ -300,8 +296,9 @@ class Backend extends Controller
                     $where[] = [$k, $sym, intval($v)];
                     $where[] = [$k, $sym, intval($v)];
                     break;
                     break;
                 case 'FINDIN':
                 case 'FINDIN':
+                case 'FINDINSET':
                 case 'FIND_IN_SET':
                 case 'FIND_IN_SET':
-                    $where[] = "FIND_IN_SET('{$v}', `{$k}`)";
+                    $where[] = "FIND_IN_SET('{$v}', " . ($this->relationSearch ? $k : '`' . str_replace('.', '`.`', $k) . '`') . ")";
                     break;
                     break;
                 case 'IN':
                 case 'IN':
                 case 'IN(...)':
                 case 'IN(...)':
@@ -315,13 +312,10 @@ class Backend extends Controller
                     if (stripos($v, ',') === false || !array_filter($arr))
                     if (stripos($v, ',') === false || !array_filter($arr))
                         continue;
                         continue;
                     //当出现一边为空时改变操作符
                     //当出现一边为空时改变操作符
-                    if ($arr[0] === '')
-                    {
+                    if ($arr[0] === '') {
                         $sym = $sym == 'BETWEEN' ? '<=' : '>';
                         $sym = $sym == 'BETWEEN' ? '<=' : '>';
                         $arr = $arr[1];
                         $arr = $arr[1];
-                    }
-                    else if ($arr[1] === '')
-                    {
+                    } else if ($arr[1] === '') {
                         $sym = $sym == 'BETWEEN' ? '>=' : '<';
                         $sym = $sym == 'BETWEEN' ? '>=' : '<';
                         $arr = $arr[0];
                         $arr = $arr[0];
                     }
                     }
@@ -334,13 +328,10 @@ class Backend extends Controller
                     if (stripos($v, ',') === false || !array_filter($arr))
                     if (stripos($v, ',') === false || !array_filter($arr))
                         continue;
                         continue;
                     //当出现一边为空时改变操作符
                     //当出现一边为空时改变操作符
-                    if ($arr[0] === '')
-                    {
+                    if ($arr[0] === '') {
                         $sym = $sym == 'RANGE' ? '<=' : '>';
                         $sym = $sym == 'RANGE' ? '<=' : '>';
                         $arr = $arr[1];
                         $arr = $arr[1];
-                    }
-                    else if ($arr[1] === '')
-                    {
+                    } else if ($arr[1] === '') {
                         $sym = $sym == 'RANGE' ? '>=' : '<';
                         $sym = $sym == 'RANGE' ? '>=' : '<';
                         $arr = $arr[0];
                         $arr = $arr[0];
                     }
                     }
@@ -360,15 +351,11 @@ class Backend extends Controller
                     break;
                     break;
             }
             }
         }
         }
-        $where = function($query) use ($where) {
-            foreach ($where as $k => $v)
-            {
-                if (is_array($v))
-                {
+        $where = function ($query) use ($where) {
+            foreach ($where as $k => $v) {
+                if (is_array($v)) {
                     call_user_func_array([$query, 'where'], $v);
                     call_user_func_array([$query, 'where'], $v);
-                }
-                else
-                {
+                } else {
                     $query->where($v);
                     $query->where($v);
                 }
                 }
             }
             }
@@ -383,17 +370,14 @@ class Backend extends Controller
      */
      */
     protected function getDataLimitAdminIds()
     protected function getDataLimitAdminIds()
     {
     {
-        if (!$this->dataLimit)
-        {
+        if (!$this->dataLimit) {
             return null;
             return null;
         }
         }
-        if ($this->auth->isSuperAdmin())
-        {
+        if ($this->auth->isSuperAdmin()) {
             return null;
             return null;
         }
         }
         $adminIds = [];
         $adminIds = [];
-        if (in_array($this->dataLimit, ['auth', 'personal']))
-        {
+        if (in_array($this->dataLimit, ['auth', 'personal'])) {
             $adminIds = $this->dataLimit == 'auth' ? $this->auth->getChildrenAdminIds(true) : [$this->auth->id];
             $adminIds = $this->dataLimit == 'auth' ? $this->auth->getChildrenAdminIds(true) : [$this->auth->id];
         }
         }
         return $adminIds;
         return $adminIds;
@@ -401,10 +385,10 @@ class Backend extends Controller
 
 
     /**
     /**
      * Selectpage的实现方法
      * Selectpage的实现方法
-     * 
+     *
      * 当前方法只是一个比较通用的搜索匹配,请按需重载此方法来编写自己的搜索逻辑,$where按自己的需求写即可
      * 当前方法只是一个比较通用的搜索匹配,请按需重载此方法来编写自己的搜索逻辑,$where按自己的需求写即可
      * 这里示例了所有的参数,所以比较复杂,实现上自己实现只需简单的几行即可
      * 这里示例了所有的参数,所以比较复杂,实现上自己实现只需简单的几行即可
-     * 
+     *
      */
      */
     protected function selectpage()
     protected function selectpage()
     {
     {
@@ -412,7 +396,7 @@ class Backend extends Controller
         $this->request->filter(['strip_tags', 'htmlspecialchars']);
         $this->request->filter(['strip_tags', 'htmlspecialchars']);
 
 
         //搜索关键词,客户端输入以空格分开,这里接收为数组
         //搜索关键词,客户端输入以空格分开,这里接收为数组
-        $word = (array) $this->request->request("q_word/a");
+        $word = (array)$this->request->request("q_word/a");
         //当前页
         //当前页
         $page = $this->request->request("pageNumber");
         $page = $this->request->request("pageNumber");
         //分页大小
         //分页大小
@@ -420,7 +404,7 @@ class Backend extends Controller
         //搜索条件
         //搜索条件
         $andor = $this->request->request("andOr");
         $andor = $this->request->request("andOr");
         //排序方式
         //排序方式
-        $orderby = (array) $this->request->request("orderBy/a");
+        $orderby = (array)$this->request->request("orderBy/a");
         //显示的字段
         //显示的字段
         $field = $this->request->request("showField");
         $field = $this->request->request("showField");
         //主键
         //主键
@@ -428,59 +412,48 @@ class Backend extends Controller
         //主键值
         //主键值
         $primaryvalue = $this->request->request("keyValue");
         $primaryvalue = $this->request->request("keyValue");
         //搜索字段
         //搜索字段
-        $searchfield = (array) $this->request->request("searchField/a");
+        $searchfield = (array)$this->request->request("searchField/a");
         //自定义搜索条件
         //自定义搜索条件
-        $custom = (array) $this->request->request("custom/a");
+        $custom = (array)$this->request->request("custom/a");
         $order = [];
         $order = [];
-        foreach ($orderby as $k => $v)
-        {
+        foreach ($orderby as $k => $v) {
             $order[$v[0]] = $v[1];
             $order[$v[0]] = $v[1];
         }
         }
         $field = $field ? $field : 'name';
         $field = $field ? $field : 'name';
 
 
         //如果有primaryvalue,说明当前是初始化传值
         //如果有primaryvalue,说明当前是初始化传值
-        if ($primaryvalue !== null)
-        {
+        if ($primaryvalue !== null) {
             $where = [$primarykey => ['in', $primaryvalue]];
             $where = [$primarykey => ['in', $primaryvalue]];
-        }
-        else
-        {
-            $where = function($query) use($word, $andor, $field, $searchfield, $custom) {
-                foreach ($word as $k => $v)
-                {
-                    foreach ($searchfield as $m => $n)
-                    {
+        } else {
+            $where = function ($query) use ($word, $andor, $field, $searchfield, $custom) {
+                foreach ($word as $k => $v) {
+                    foreach ($searchfield as $m => $n) {
                         $query->where($n, "like", "%{$v}%", $andor);
                         $query->where($n, "like", "%{$v}%", $andor);
                     }
                     }
                 }
                 }
-                if ($custom && is_array($custom))
-                {
-                    foreach ($custom as $k => $v)
-                    {
+                if ($custom && is_array($custom)) {
+                    foreach ($custom as $k => $v) {
                         $query->where($k, '=', $v);
                         $query->where($k, '=', $v);
                     }
                     }
                 }
                 }
             };
             };
         }
         }
         $adminIds = $this->getDataLimitAdminIds();
         $adminIds = $this->getDataLimitAdminIds();
-        if (is_array($adminIds))
-        {
+        if (is_array($adminIds)) {
             $this->model->where($this->dataLimitField, 'in', $adminIds);
             $this->model->where($this->dataLimitField, 'in', $adminIds);
         }
         }
         $list = [];
         $list = [];
         $total = $this->model->where($where)->count();
         $total = $this->model->where($where)->count();
-        if ($total > 0)
-        {
-            if (is_array($adminIds))
-            {
+        if ($total > 0) {
+            if (is_array($adminIds)) {
                 $this->model->where($this->dataLimitField, 'in', $adminIds);
                 $this->model->where($this->dataLimitField, 'in', $adminIds);
             }
             }
             $list = $this->model->where($where)
             $list = $this->model->where($where)
-                    ->order($order)
-                    ->page($page, $pagesize)
-                    ->field("{$primarykey},{$field}")
-                    ->field("password,salt", true)
-                    ->select();
+                ->order($order)
+                ->page($page, $pagesize)
+                ->field("{$primarykey},{$field}")
+                ->field("password,salt", true)
+                ->select();
         }
         }
         //这里一定要返回有list这个字段,total是可选的,如果total<=list的数量,则会隐藏分页按钮
         //这里一定要返回有list这个字段,total是可选的,如果total<=list的数量,则会隐藏分页按钮
         return json(['list' => $list, 'total' => $total]);
         return json(['list' => $list, 'total' => $total]);

+ 11 - 0
application/index/lang/zh-cn.php

@@ -63,6 +63,17 @@ return [
     'Content'                                                => '内容',
     'Content'                                                => '内容',
     'Status'                                                 => '状态',
     'Status'                                                 => '状态',
     'Operate'                                                => '操作',
     'Operate'                                                => '操作',
+    'Apply'                                                  => '应用',
+    'Cancel'                                                 => '取消',
+    'Clear'                                                  => '清空',
+    'Custom Range'                                           => '自定义',
+    'Today'                                                  => '今天',
+    'Yesterday'                                              => '昨天',
+    'Last 7 days'                                            => '最近7天',
+    'Last 30 days'                                           => '最近30天',
+    'Last month'                                             => '上月',
+    'This month'                                             => '本月',
+    'Choose'                                                 => '选择',
     'Append'                                                 => '追加',
     'Append'                                                 => '追加',
     'Memo'                                                   => '备注',
     'Memo'                                                   => '备注',
     'Parent'                                                 => '父级',
     'Parent'                                                 => '父级',

+ 8 - 0
public/assets/css/backend.css

@@ -649,6 +649,14 @@ form.form-horizontal .control-label {
 .bootstrap-table .fa-toggle-on.fa-2x {
 .bootstrap-table .fa-toggle-on.fa-2x {
   font-size: 1.86em;
   font-size: 1.86em;
 }
 }
+.bootstrap-table .form-commonsearch .form-group {
+  margin-left: 0;
+  margin-right: 0;
+}
+.bootstrap-table .form-commonsearch .form-group > .control-label {
+  padding-left: 0;
+  padding-right: 0;
+}
 .toolbar {
 .toolbar {
   margin-top: 10px;
   margin-top: 10px;
   margin-bottom: 10px;
   margin-bottom: 10px;

File diff suppressed because it is too large
+ 1 - 1
public/assets/css/backend.min.css


+ 46 - 29
public/assets/js/backend/addon.js

@@ -173,11 +173,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
 
 
             // 会员信息
             // 会员信息
             $(document).on("click", ".btn-userinfo", function () {
             $(document).on("click", ".btn-userinfo", function () {
+                var that = this;
                 var userinfo = Controller.api.userinfo.get();
                 var userinfo = Controller.api.userinfo.get();
                 if (!userinfo) {
                 if (!userinfo) {
                     Layer.open({
                     Layer.open({
                         content: Template("logintpl", {}),
                         content: Template("logintpl", {}),
-                        area: ['400px', '330px'],
+                        area: ['430px', '350px'],
                         title: __('Login FastAdmin'),
                         title: __('Login FastAdmin'),
                         resize: false,
                         resize: false,
                         btn: [__('Login'), __('Register')],
                         btn: [__('Login'), __('Register')],
@@ -206,33 +207,43 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                         }
                         }
                     });
                     });
                 } else {
                 } else {
-                    var userinfo = Controller.api.userinfo.get();
-                    if (!userinfo) {
-                        Layer.alert(__('You\'re not login'));
-                        return false;
-                    }
-                    Layer.open({
-                        content: Template("userinfotpl", userinfo),
-                        area: ['400px', '330px'],
-                        title: __('Userinfo'),
-                        resize: false,
-                        btn: [__('Logout'), __('Cancel')],
-                        yes: function () {
-                            Fast.api.ajax({
-                                url: Config.fastadmin.api_url + '/user/logout',
-                                dataType: 'jsonp',
-                                data: {uid: userinfo.id, token: userinfo.token}
-                            }, function (data, ret) {
-                                Controller.api.userinfo.set(null);
-                                Layer.closeAll();
-                                Layer.alert(ret.msg);
-                            }, function (data, ret) {
-                                Controller.api.userinfo.set(null);
-                                Layer.closeAll();
-                                Layer.alert(ret.msg);
-                            });
+                    Fast.api.ajax({
+                        url: Config.fastadmin.api_url + '/user/index',
+                        dataType: 'jsonp',
+                        data: {
+                            user_id: userinfo.id,
+                            token: userinfo.token,
                         }
                         }
+                    }, function (data) {
+                        Layer.open({
+                            content: Template("userinfotpl", userinfo),
+                            area: ['430px', '360px'],
+                            title: __('Userinfo'),
+                            resize: false,
+                            btn: [__('Logout'), __('Cancel')],
+                            yes: function () {
+                                Fast.api.ajax({
+                                    url: Config.fastadmin.api_url + '/user/logout',
+                                    dataType: 'jsonp',
+                                    data: {uid: userinfo.id, token: userinfo.token}
+                                }, function (data, ret) {
+                                    Controller.api.userinfo.set(null);
+                                    Layer.closeAll();
+                                    Layer.alert(ret.msg);
+                                }, function (data, ret) {
+                                    Controller.api.userinfo.set(null);
+                                    Layer.closeAll();
+                                    Layer.alert(ret.msg);
+                                });
+                            }
+                        });
+                        return false;
+                    }, function (data) {
+                        Controller.api.userinfo.set(null);
+                        $(that).trigger('click');
+                        return false;
                     });
                     });
+
                 }
                 }
             });
             });
 
 
@@ -278,6 +289,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                             }
                             }
                         });
                         });
                     } else if (ret && ret.code === -2) {
                     } else if (ret && ret.code === -2) {
+                        //如果登录已经超时,重新提醒登录
+                        if (uid && uid != ret.data.uid) {
+                            Controller.api.userinfo.set(null);
+                            $(".operate[data-name='" + name + "'] .btn-install").trigger("click");
+                            return;
+                        }
                         top.Fast.api.open(ret.data.payurl, __('Pay now'), {
                         top.Fast.api.open(ret.data.payurl, __('Pay now'), {
                             area: ["650px", "700px"],
                             area: ["650px", "700px"],
                             end: function () {
                             end: function () {
@@ -425,7 +442,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                     Layer.alert(__('Please disable addon first'), {icon: 7});
                     Layer.alert(__('Please disable addon first'), {icon: 7});
                     return false;
                     return false;
                 }
                 }
-                Layer.confirm(__('Uninstall tips'), function () {
+                Layer.confirm(__('Uninstall tips', Config['addons'][name].title), function () {
                     uninstall(name, false);
                     uninstall(name, false);
                 });
                 });
             });
             });
@@ -452,7 +469,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                 }
                 }
                 var version = $(this).data("version");
                 var version = $(this).data("version");
 
 
-                Layer.confirm(__('Upgrade tips'), function () {
+                Layer.confirm(__('Upgrade tips', Config['addons'][name].title), function () {
                     upgrade(name, version);
                     upgrade(name, version);
                 });
                 });
             });
             });
@@ -510,7 +527,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                     return value;
                     return value;
                 },
                 },
                 version: function (value, row, index) {
                 version: function (value, row, index) {
-                    return row.addon && row.addon.version != row.version ? '<span class="releasetips" data-toggle="tooltip" title="' + __('New version') + ':' + row.version + '">' + row.addon.version + '<i></i></span>' : row.version;
+                    return row.addon && row.addon.version != row.version ? '<a href="' + row.url + '?version=' + row.version + '" target="_blank"><span class="releasetips text-primary" data-toggle="tooltip" title="' + __('New version tips', row.version) + '">' + row.addon.version + '<i></i></span></a>' : row.version;
                 },
                 },
                 home: function (value, row, index) {
                 home: function (value, row, index) {
                     return row.addon ? '<a href="' + row.addon.url + '" data-toggle="tooltip" title="' + __('View addon index page') + '" target="_blank"><i class="fa fa-home text-primary"></i></a>' : '<a href="javascript:;"><i class="fa fa-home text-gray"></i></a>';
                     return row.addon ? '<a href="' + row.addon.url + '" data-toggle="tooltip" title="' + __('View addon index page') + '" target="_blank"><i class="fa fa-home text-primary"></i></a>' : '<a href="javascript:;"><i class="fa fa-home text-gray"></i></a>';

+ 35 - 31
public/assets/js/bootstrap-table-commonsearch.js

@@ -78,39 +78,27 @@
                 var style = typeof vObjCol.style === 'undefined' ? '' : sprintf('style="%s"', vObjCol.style);
                 var style = typeof vObjCol.style === 'undefined' ? '' : sprintf('style="%s"', vObjCol.style);
                 extend = typeof vObjCol.data !== 'undefined' && extend == '' ? vObjCol.data : extend;
                 extend = typeof vObjCol.data !== 'undefined' && extend == '' ? vObjCol.data : extend;
                 if (vObjCol.searchList) {
                 if (vObjCol.searchList) {
-                    if (typeof vObjCol.searchList === 'object' && typeof vObjCol.searchList.then === 'function') {
-                        htmlForm.push(sprintf('<select class="%s" name="%s" %s %s>%s</select>', addClass, vObjCol.field, style, extend, sprintf('<option value="">%s</option>', that.options.formatCommonChoose())));
-                        (function (vObjCol, that) {
-                            $.when(vObjCol.searchList).done(function (ret) {
-                                var isArray = false;
-                                if (ret.data && ret.data.searchlist && $.isArray(ret.data.searchlist)) {
-                                    var resultlist = {};
-                                    $.each(ret.data.searchlist, function (key, value) {
-                                        resultlist[value.id] = value.name;
-                                    });
-                                } else if (ret.constructor === Array || ret.constructor === Object) {
-                                    var resultlist = ret;
-                                    isArray = ret.constructor === Array ? true : isArray;
-                                }
-                                var optionList = [];
-                                $.each(resultlist, function (key, value) {
-                                    var isSelect = (isArray ? value : key) == vObjCol.defaultValue ? 'selected' : '';
-                                    optionList.push(sprintf("<option value='" + (isArray ? value : key) + "' %s>" + value + "</option>", isSelect));
-                                });
-                                $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).append(optionList.join(''));
-                            });
-                        })(vObjCol, that);
-                    } else if (typeof vObjCol.searchList == 'function') {
+                    if (typeof vObjCol.searchList === 'function') {
                         htmlForm.push(vObjCol.searchList.call(this, vObjCol));
                         htmlForm.push(vObjCol.searchList.call(this, vObjCol));
                     } else {
                     } else {
-                        var isArray = vObjCol.searchList.constructor === Array;
-                        var searchList = [];
-                        searchList.push(sprintf('<option value="">%s</option>', that.options.formatCommonChoose()));
-                        $.each(vObjCol.searchList, function (key, value) {
-                            var isSelect = (isArray ? value : key) == vObjCol.defaultValue ? 'selected' : '';
-                            searchList.push(sprintf("<option value='" + (isArray ? value : key) + "' %s>" + value + "</option>", isSelect));
-                        });
-                        htmlForm.push(sprintf('<select class="%s" name="%s" %s %s>%s</select>', addClass, vObjCol.field, style, extend, searchList.join('')));
+                        var optionList = [sprintf('<option value="">%s</option>', that.options.formatCommonChoose())];
+                        if (typeof vObjCol.searchList === 'object' && typeof vObjCol.searchList.then === 'function') {
+                            (function (vObjCol, that) {
+                                $.when(vObjCol.searchList).done(function (ret) {
+                                    var searchList = [];
+                                    if (ret.data && ret.data.searchlist && $.isArray(ret.data.searchlist)) {
+                                        searchList = ret.data.searchlist;
+                                    } else if (ret.constructor === Array || ret.constructor === Object) {
+                                        searchList = ret;
+                                    }
+                                    var optionList = createOptionList(searchList, vObjCol, that);
+                                    $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).html(optionList.join(''));
+                                });
+                            })(vObjCol, that);
+                        } else {
+                            optionList = createOptionList(vObjCol.searchList, vObjCol, that);
+                        }
+                        htmlForm.push(sprintf('<select class="%s" name="%s" %s %s>%s</select>', addClass, vObjCol.field, style, extend, optionList.join('')));
                     }
                     }
                 } else {
                 } else {
                     var placeholder = typeof vObjCol.placeholder === 'undefined' ? vObjCol.title : vObjCol.placeholder;
                     var placeholder = typeof vObjCol.placeholder === 'undefined' ? vObjCol.title : vObjCol.placeholder;
@@ -153,6 +141,22 @@
         return htmlBtn;
         return htmlBtn;
     };
     };
 
 
+    var createOptionList = function (searchList, vObjCol, that) {
+        var isArray = searchList.constructor === Array;
+        var optionList = [];
+        optionList.push(sprintf('<option value="">%s</option>', that.options.formatCommonChoose()));
+        $.each(searchList, function (key, value) {
+            if (value.constructor === Object) {
+                key = value.id;
+                value = value.name;
+            } else {
+                key = isArray ? value : key;
+            }
+            optionList.push(sprintf("<option value='" + key + "' %s>" + value + "</option>", key == vObjCol.defaultValue ? 'selected' : ''));
+        });
+        return optionList;
+    };
+
     var isSearchAvailble = function (that) {
     var isSearchAvailble = function (that) {
 
 
         //只支持服务端搜索
         //只支持服务端搜索

+ 36 - 32
public/assets/js/require-backend.min.js

@@ -8773,7 +8773,7 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                         return false;
                         return false;
                     }
                     }
                 }
                 }
-                var type = form.attr("method").toUpperCase();
+                var type = form.attr("method") ? form.attr("method").toUpperCase() : 'GET';
                 type = type && (type === 'GET' || type === 'POST') ? type : 'GET';
                 type = type && (type === 'GET' || type === 'POST') ? type : 'GET';
                 url = form.attr("action");
                 url = form.attr("action");
                 url = url ? url : location.href;
                 url = url ? url : location.href;
@@ -8944,39 +8944,27 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
                 var style = typeof vObjCol.style === 'undefined' ? '' : sprintf('style="%s"', vObjCol.style);
                 var style = typeof vObjCol.style === 'undefined' ? '' : sprintf('style="%s"', vObjCol.style);
                 extend = typeof vObjCol.data !== 'undefined' && extend == '' ? vObjCol.data : extend;
                 extend = typeof vObjCol.data !== 'undefined' && extend == '' ? vObjCol.data : extend;
                 if (vObjCol.searchList) {
                 if (vObjCol.searchList) {
-                    if (typeof vObjCol.searchList === 'object' && typeof vObjCol.searchList.then === 'function') {
-                        htmlForm.push(sprintf('<select class="%s" name="%s" %s %s>%s</select>', addClass, vObjCol.field, style, extend, sprintf('<option value="">%s</option>', that.options.formatCommonChoose())));
-                        (function (vObjCol, that) {
-                            $.when(vObjCol.searchList).done(function (ret) {
-                                var isArray = false;
-                                if (ret.data && ret.data.searchlist && $.isArray(ret.data.searchlist)) {
-                                    var resultlist = {};
-                                    $.each(ret.data.searchlist, function (key, value) {
-                                        resultlist[value.id] = value.name;
-                                    });
-                                } else if (ret.constructor === Array || ret.constructor === Object) {
-                                    var resultlist = ret;
-                                    isArray = ret.constructor === Array ? true : isArray;
-                                }
-                                var optionList = [];
-                                $.each(resultlist, function (key, value) {
-                                    var isSelect = (isArray ? value : key) == vObjCol.defaultValue ? 'selected' : '';
-                                    optionList.push(sprintf("<option value='" + (isArray ? value : key) + "' %s>" + value + "</option>", isSelect));
-                                });
-                                $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).append(optionList.join(''));
-                            });
-                        })(vObjCol, that);
-                    } else if (typeof vObjCol.searchList == 'function') {
+                    if (typeof vObjCol.searchList === 'function') {
                         htmlForm.push(vObjCol.searchList.call(this, vObjCol));
                         htmlForm.push(vObjCol.searchList.call(this, vObjCol));
                     } else {
                     } else {
-                        var isArray = vObjCol.searchList.constructor === Array;
-                        var searchList = [];
-                        searchList.push(sprintf('<option value="">%s</option>', that.options.formatCommonChoose()));
-                        $.each(vObjCol.searchList, function (key, value) {
-                            var isSelect = (isArray ? value : key) == vObjCol.defaultValue ? 'selected' : '';
-                            searchList.push(sprintf("<option value='" + (isArray ? value : key) + "' %s>" + value + "</option>", isSelect));
-                        });
-                        htmlForm.push(sprintf('<select class="%s" name="%s" %s %s>%s</select>', addClass, vObjCol.field, style, extend, searchList.join('')));
+                        var optionList = [sprintf('<option value="">%s</option>', that.options.formatCommonChoose())];
+                        if (typeof vObjCol.searchList === 'object' && typeof vObjCol.searchList.then === 'function') {
+                            (function (vObjCol, that) {
+                                $.when(vObjCol.searchList).done(function (ret) {
+                                    var searchList = [];
+                                    if (ret.data && ret.data.searchlist && $.isArray(ret.data.searchlist)) {
+                                        searchList = ret.data.searchlist;
+                                    } else if (ret.constructor === Array || ret.constructor === Object) {
+                                        searchList = ret;
+                                    }
+                                    var optionList = createOptionList(searchList, vObjCol, that);
+                                    $("form.form-commonsearch select[name='" + vObjCol.field + "']", that.$container).html(optionList.join(''));
+                                });
+                            })(vObjCol, that);
+                        } else {
+                            optionList = createOptionList(vObjCol.searchList, vObjCol, that);
+                        }
+                        htmlForm.push(sprintf('<select class="%s" name="%s" %s %s>%s</select>', addClass, vObjCol.field, style, extend, optionList.join('')));
                     }
                     }
                 } else {
                 } else {
                     var placeholder = typeof vObjCol.placeholder === 'undefined' ? vObjCol.title : vObjCol.placeholder;
                     var placeholder = typeof vObjCol.placeholder === 'undefined' ? vObjCol.title : vObjCol.placeholder;
@@ -9019,6 +9007,22 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef
         return htmlBtn;
         return htmlBtn;
     };
     };
 
 
+    var createOptionList = function (searchList, vObjCol, that) {
+        var isArray = searchList.constructor === Array;
+        var optionList = [];
+        optionList.push(sprintf('<option value="">%s</option>', that.options.formatCommonChoose()));
+        $.each(searchList, function (key, value) {
+            if (value.constructor === Object) {
+                key = value.id;
+                value = value.name;
+            } else {
+                key = isArray ? value : key;
+            }
+            optionList.push(sprintf("<option value='" + key + "' %s>" + value + "</option>", key == vObjCol.defaultValue ? 'selected' : ''));
+        });
+        return optionList;
+    };
+
     var isSearchAvailble = function (that) {
     var isSearchAvailble = function (that) {
 
 
         //只支持服务端搜索
         //只支持服务端搜索

+ 1 - 1
public/assets/js/require-form.js

@@ -357,7 +357,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
                         return false;
                         return false;
                     }
                     }
                 }
                 }
-                var type = form.attr("method").toUpperCase();
+                var type = form.attr("method") ? form.attr("method").toUpperCase() : 'GET';
                 type = type && (type === 'GET' || type === 'POST') ? type : 'GET';
                 type = type && (type === 'GET' || type === 'POST') ? type : 'GET';
                 url = form.attr("action");
                 url = form.attr("action");
                 url = url ? url : location.href;
                 url = url ? url : location.href;

+ 11 - 1
public/assets/less/backend.less

@@ -730,8 +730,18 @@ form.form-horizontal .control-label {
 .bootstrap-table .fixed-table-toolbar .dropdown-menu {
 .bootstrap-table .fixed-table-toolbar .dropdown-menu {
   overflow: auto;
   overflow: auto;
 }
 }
+
 .bootstrap-table .fa-toggle-on.fa-2x {
 .bootstrap-table .fa-toggle-on.fa-2x {
-  font-size:1.86em;
+  font-size: 1.86em;
+}
+
+.bootstrap-table .form-commonsearch .form-group {
+  margin-left: 0;
+  margin-right: 0;
+  > .control-label {
+    padding-left:0;
+    padding-right:0;
+  }
 }
 }
 
 
 .toolbar {
 .toolbar {