Browse Source

优化安装脚本
优化cookie加密
修复系统配置关联字段链接错误BUG
修复addtion方法空数据错误的BUG
修复后台管理获取子级分组列表BUG
优化邮件发送方式
优化cdnurl判断
优化插件管理提示
优化会员附件列表搜索
优化会员登录注册跳转链接获取

Karson 4 years ago
parent
commit
4a9ebb9c7d

+ 1 - 0
.gitignore

@@ -15,3 +15,4 @@ composer.lock
 .env
 .svn
 .vscode
+node_modules

+ 2 - 2
application/admin/command/Install/fastadmin.sql

@@ -340,11 +340,11 @@ INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array
 INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '请尽量输入左侧菜单栏存在的链接', 'string', 'dashboard', '', 'required', '', '');
 INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '', '');
 INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '', '');
-INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '1', '[\"请选择\",\"SMTP\",\"Mail\"]', '', '', '');
+INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '1', '[\"请选择\",\"SMTP\"]', '', '', '');
 INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', 'smtp.qq.com', '', '', '', '');
 INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '465', '', '', '', '');
 INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '10000', '', '', '', '');
-INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码)', 'string', 'password', '', '', '', '');
+INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码或授权码)', 'string', 'password', '', '', '', '');
 INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', '(SMTP验证方式[推荐SSL])', 'select', '2', '[\"无\",\"TLS\",\"SSL\"]', '', '', '');
 INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '10000@qq.com', '', '', '', '');
 COMMIT;

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

@@ -15,7 +15,7 @@ use think\Exception;
  * 插件管理
  *
  * @icon   fa fa-cube
- * @remark 可在线安装、卸载、禁用、启用插件,同时支持添加本地插件。FastAdmin已上线插件商店 ,你可以发布你的免费或付费插件:<a href="https://www.fastadmin.net/store.html" target="_blank">https://www.fastadmin.net/store.html</a>
+ * @remark 可在线安装、卸载、禁用、启用、配置、升级插件,插件升级前请做好备份
  */
 class Addon extends Backend
 {

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

@@ -10,7 +10,7 @@ use fast\Tree;
  * 分类管理
  *
  * @icon   fa fa-list
- * @remark 用于统一管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加
+ * @remark 用于管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加
  */
 class Category extends Backend
 {

+ 2 - 2
application/admin/controller/general/Config.php

@@ -255,8 +255,8 @@ class Config extends Backend
             $email = new Email;
             $result = $email
                 ->to($receiver)
-                ->subject(__("This is a test mail"))
-                ->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . __('This is a test mail content') . '</div>')
+                ->subject(__("This is a test mail", config('site.name')))
+                ->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . __('This is a test mail content', config('site.name')) . '</div>')
                 ->send();
             if ($result) {
                 $this->success();

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

@@ -194,9 +194,9 @@ return [
     'Third group 2'                                         => '三级管理组2',
     'Dashboard tips'                                        => '用于展示当前系统中的统计数据、统计报表及重要实时数据',
     'Config tips'                                           => '可以在此增改系统的变量和分组,也可以自定义分组和变量',
-    'Category tips'                                         => '用于统一管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加',
+    'Category tips'                                         => '用于管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加',
     'Attachment tips'                                       => '主要用于管理上传到服务器或第三方存储的数据',
-    'Addon tips'                                            => '可在线安装、卸载、禁用、启用插件,同时支持添加本地插件。',
+    'Addon tips'                                            => '可在线安装、卸载、禁用、启用、配置、升级插件,插件升级前请做好备份。',
     'Admin tips'                                            => '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成',
     'Admin log tips'                                        => '管理员可以查看自己所拥有的权限的管理员日志',
     'Group tips'                                            => '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员',

+ 1 - 1
application/admin/lang/zh-cn/addon.php

@@ -25,7 +25,7 @@ return [
     'Offline installed tips'                                  => '安装成功!清除浏览器缓存和框架缓存后生效!',
     'Online installed tips'                                   => '安装成功!清除浏览器缓存和框架缓存后生效!',
     'Not login tips'                                          => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!',
-    'Please login and try to install'                         => '请登录后再进行离线安装!',
+    'Please login and try to install'                         => '请登录FastAdmin后再进行离线安装!',
     'Not installed tips'                                      => '请安装后再访问插件前台页面!',
     'Not enabled tips'                                        => '插件已经禁用,请启用后再访问插件前台页面!',
     'New version tips'                                        => '发现新版本:%s 点击查看更新日志',

+ 2 - 2
application/admin/lang/zh-cn/general/config.php

@@ -70,8 +70,8 @@ return [
     'Name already exist'          => '变量名称已经存在',
     'Add new config'              => '点击添加新的配置',
     'Send a test message'         => '发送测试邮件',
-    'This is a test mail content' => '这是一封来自FastAdmin校验邮件,用于校验邮件配置是否正常!',
-    'This is a test mail'         => '这是一封来自FastAdmin的邮件',
+    'This is a test mail content' => '这是一封来自{sitename}校验邮件,用于校验邮件配置是否正常!',
+    'This is a test mail'         => '这是一封来自{sitename}的邮件',
     'Please input your email'     => '请输入测试接收者邮箱',
     'Please input correct email'  => '请输入正确的邮箱地址',
 ];

+ 5 - 5
application/admin/library/Auth.php

@@ -100,7 +100,7 @@ class Auth extends \fast\Auth
                 return false;
             }
             //token有变更
-            if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token)) {
+            if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token . config('token.key'))) {
                 return false;
             }
             $ip = request()->ip();
@@ -127,9 +127,9 @@ class Auth extends \fast\Auth
     {
         if ($keeptime) {
             $expiretime = time() + $keeptime;
-            $key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token);
+            $key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token . config('token.key'));
             $data = [$this->id, $keeptime, $expiretime, $key];
-            Cookie::set('keeplogin', implode('|', $data), 86400 * 30);
+            Cookie::set('keeplogin', implode('|', $data), 86400 * 7);
             return true;
         }
         return false;
@@ -292,8 +292,8 @@ class Auth extends \fast\Auth
                 break;
             }
             // 取出包含自己的所有子节点
-            $childrenList = Tree::instance()->init($groupList)->getChildren($v['id'], true);
-            $obj = Tree::instance()->init($childrenList)->getTreeArray($v['pid']);
+            $childrenList = Tree::instance()->init($groupList, 'pid')->getChildren($v['id'], true);
+            $obj = Tree::instance()->init($childrenList, 'pid')->getTreeArray($v['pid']);
             $objList = array_merge($objList, Tree::instance()->getTreeList($obj));
         }
         $childrenGroupIds = [];

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

@@ -160,7 +160,7 @@
                                             {/case}
                                             {case value="selectpage" break="0"}{/case}
                                             {case value="selectpages"}
-                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" id="c-{$item.name}" value="{$item.value|htmlentities}" class="form-control selectpage" data-source="{:url('general/config/selectpage')}?id={$item.id}" data-primary-key="{$item.setting.primarykey}" data-field="{$item.setting.field}" data-multiple="{$item.type=='selectpage'?'false':'true'}" data-tip="{$item.tip}" data-rule="{$item.rule}" />
+                                            <input {$item.extend_html} type="text" name="row[{$item.name}]" id="c-{$item.name}" value="{$item.value|htmlentities}" class="form-control selectpage" data-source="{:url('general.config/selectpage')}?id={$item.id}" data-primary-key="{$item.setting.primarykey}" data-field="{$item.setting.field}" data-multiple="{$item.type=='selectpage'?'false':'true'}" data-tip="{$item.tip}" data-rule="{$item.rule}" />
                                             {/case}
                                             {case custom}
                                             {$item.extend_html}

+ 63 - 7
application/common.php

@@ -85,7 +85,8 @@ if (!function_exists('cdnurl')) {
     function cdnurl($url, $domain = false)
     {
         $regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
-        $url = preg_match($regex, $url) ? $url : \think\Config::get('upload.cdnurl') . $url;
+        $cdnurl = \think\Config::get('upload.cdnurl');
+        $url = preg_match($regex, $url) || ($cdnurl && stripos($url, $cdnurl) === 0) ? $url : $cdnurl . $url;
         if ($domain && !preg_match($regex, $url)) {
             $domain = is_bool($domain) ? request()->domain() : $domain;
             $url = $domain . $url;
@@ -246,7 +247,7 @@ if (!function_exists('addtion')) {
                 $model = $v['name'] ? \think\Db::name($v['name']) : \think\Db::table($v['table']);
             }
             $primary = $v['primary'] ? $v['primary'] : $model->getPk();
-            $result[$v['field']] = $model->where($primary, 'in', $ids[$v['field']])->column("{$primary},{$v['column']}");
+            $result[$v['field']] = isset($ids[$v['field']]) ? $model->where($primary, 'in', $ids[$v['field']])->column("{$primary},{$v['column']}") : [];
         }
 
         foreach ($items as $k => &$v) {
@@ -265,13 +266,68 @@ if (!function_exists('addtion')) {
 if (!function_exists('var_export_short')) {
 
     /**
-     * 返回打印数组结构
-     * @param string $var 数组
+     * 使用短标签打印或返回数组结构
+     * @param mixed   $data
+     * @param boolean $return 是否返回数据
      * @return string
      */
-    function var_export_short($var)
+    function var_export_short($data, $return = true)
     {
-        return VarExporter::export($var);
+        return var_export($data, $return);
+        $replaced = [];
+        $count = 0;
+
+        //判断是否是对象
+        if (is_resource($data) || is_object($data)) {
+            return var_export($data, $return);
+        }
+
+        //判断是否有特殊的键名
+        $specialKey = false;
+        array_walk_recursive($data, function (&$value, &$key) use (&$specialKey) {
+            if (is_string($key) && (stripos($key, "\n") !== false || stripos($key, "array (") !== false)) {
+                $specialKey = true;
+            }
+        });
+        if ($specialKey) {
+            return var_export($data, $return);
+        }
+        array_walk_recursive($data, function (&$value, &$key) use (&$replaced, &$count, &$stringcheck) {
+            if (is_object($value) || is_resource($value)) {
+                $replaced[$count] = var_export($value, true);
+                $value = "##<{$count}>##";
+            } else {
+                if (is_string($value) && (stripos($value, "\n") !== false || stripos($value, "array (") !== false)) {
+                    $index = array_search($value, $replaced);
+                    if ($index === false) {
+                        $replaced[$count] = var_export($value, true);
+                        $value = "##<{$count}>##";
+                    } else {
+                        $value = "##<{$index}>##";
+                    }
+                }
+            }
+            $count++;
+        });
+
+        $dump = var_export($data, true);
+
+        $dump = preg_replace('#(?:\A|\n)([ ]*)array \(#i', '[', $dump); // Starts
+        $dump = preg_replace('#\n([ ]*)\),#', "\n$1],", $dump); // Ends
+        $dump = preg_replace('#=> \[\n\s+\],\n#', "=> [],\n", $dump); // Empties
+        $dump = preg_replace('#\)$#', "]", $dump); //End
+
+        if ($replaced) {
+            $dump = preg_replace_callback("/'##<(\d+)>##'/", function ($matches) use ($replaced) {
+                return isset($replaced[$matches[1]]) ? $replaced[$matches[1]] : "''";
+            }, $dump);
+        }
+
+        if ($return === true) {
+            return $dump;
+        } else {
+            echo $dump;
+        }
     }
 }
 
@@ -290,7 +346,7 @@ if (!function_exists('letter_avatar')) {
         $bg = "rgb({$r},{$g},{$b})";
         $color = "#ffffff";
         $first = mb_strtoupper(mb_substr($text, 0, 1));
-        $src = base64_encode('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100" width="100"><rect fill="' . $bg . '" x="0" y="0" width="100" height="100"></rect><text x="50" y="50" font-size="50" text-copy="fast" fill="' . $color . '" text-anchor="middle" text-rights="admin" alignment-baseline="central">' . $first . '</text></svg>');
+        $src = base64_encode('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100" width="100"><rect fill="' . $bg . '" x="0" y="0" width="100" height="100"></rect><text x="50" y="50" font-size="50" text-copy="fast" fill="' . $color . '" text-anchor="middle" text-rights="admin" dominant-baseline="central">' . $first . '</text></svg>');
         $value = 'data:image/svg+xml;base64,' . $src;
         return $value;
     }

+ 38 - 40
application/common/library/Email.php

@@ -3,6 +3,9 @@
 namespace app\common\library;
 
 use think\Config;
+use Tx\Mailer;
+use Tx\Mailer\Exceptions\CodeException;
+use Tx\Mailer\Exceptions\SendException;
 
 class Email
 {
@@ -20,14 +23,15 @@ class Email
     /**
      * 错误内容
      */
-    protected $_error = '';
+    protected $error = '';
 
     /**
      * 默认配置
      */
     public $options = [
-        'charset' => 'utf-8', //编码格式
-        'debug'   => false, //调式模式
+        'charset'   => 'utf-8', //编码格式
+        'debug'     => false, //调式模式
+        'mail_type' => 0, //状态
     ];
 
     /**
@@ -55,22 +59,12 @@ class Email
             $this->options = array_merge($this->options, $config);
         }
         $this->options = array_merge($this->options, $options);
-        $securArr = [1 => 'tls', 2 => 'ssl'];
-
-        $this->mail = new \PHPMailer\PHPMailer\PHPMailer(true);
-        $this->mail->CharSet = $this->options['charset'];
-        if ($this->options['mail_type'] == 1) {
-            $this->mail->SMTPDebug = $this->options['debug'];
-            $this->mail->isSMTP();
-            $this->mail->SMTPAuth = true;
-        } else {
-            $this->mail->isMail();
-        }
-        $this->mail->Host = $this->options['mail_smtp_host'];
-        $this->mail->Username = $this->options['mail_from'];
-        $this->mail->Password = $this->options['mail_smtp_pass'];
-        $this->mail->SMTPSecure = isset($securArr[$this->options['mail_verify_type']]) ? $securArr[$this->options['mail_verify_type']] : '';
-        $this->mail->Port = $this->options['mail_smtp_port'];
+        $secureArr = [0 => '', 1 => 'tls', 2 => 'ssl'];
+        $secure = isset($secureArr[$this->options['mail_verify_type']]) ? $secureArr[$this->options['mail_verify_type']] : '';
+
+        $this->mail = new Mailer(new Log);
+        $this->mail->setServer($this->options['mail_smtp_host'], $this->options['mail_smtp_port'], $secure);
+        $this->mail->setAuth($this->options['mail_from'], $this->options['mail_smtp_pass']);
 
         //设置发件人
         $this->from($this->options['mail_from'], $this->options['mail_smtp_user']);
@@ -83,7 +77,7 @@ class Email
      */
     public function subject($subject)
     {
-        $this->mail->Subject = $subject;
+        $this->mail->setSubject($subject);
         return $this;
     }
 
@@ -95,7 +89,7 @@ class Email
      */
     public function from($email, $name = '')
     {
-        $this->mail->setFrom($email, $name);
+        $this->mail->setFrom($name, $email);
         return $this;
     }
 
@@ -109,7 +103,7 @@ class Email
     {
         $emailArr = $this->buildAddress($email);
         foreach ($emailArr as $address => $name) {
-            $this->mail->addAddress($address, $name);
+            $this->mail->addTo($name, $address);
         }
 
         return $this;
@@ -124,6 +118,9 @@ class Email
     public function cc($email, $name = '')
     {
         $emailArr = $this->buildAddress($email);
+        if (count($emailArr) == 1 && $name) {
+            $emailArr[key($emailArr)] = $name;
+        }
         foreach ($emailArr as $address => $name) {
             $this->mail->addCC($address, $name);
         }
@@ -139,8 +136,11 @@ class Email
     public function bcc($email, $name = '')
     {
         $emailArr = $this->buildAddress($email);
+        if (count($emailArr) == 1 && $name) {
+            $emailArr[key($emailArr)] = $name;
+        }
         foreach ($emailArr as $address => $name) {
-            $this->mail->addBCC($address, $name);
+            $this->mail->addBCC($name, $address);
         }
         return $this;
     }
@@ -153,11 +153,7 @@ class Email
      */
     public function message($body, $ishtml = true)
     {
-        if ($ishtml) {
-            $this->mail->msgHTML($body);
-        } else {
-            $this->mail->Body = $body;
-        }
+        $this->mail->setBody($body);
         return $this;
     }
 
@@ -169,7 +165,7 @@ class Email
      */
     public function attachment($path, $name = '')
     {
-        $this->mail->addAttachment($path, $name);
+        $this->mail->addAttachment($name, $path);
         return $this;
     }
 
@@ -180,13 +176,8 @@ class Email
      */
     protected function buildAddress($emails)
     {
-        $emails = is_array($emails) ? $emails : explode(',', str_replace(";", ",", $emails));
-        $result = [];
-        foreach ($emails as $key => $value) {
-            $email = is_numeric($key) ? $value : $key;
-            $result[$email] = is_numeric($key) ? "" : $value;
-        }
-        return $result;
+        $emails = is_array($emails) ? $emails : array_flip(explode(',', str_replace(";", ",", $emails)));
+        return $emails;
     }
 
     /**
@@ -195,7 +186,7 @@ class Email
      */
     public function getError()
     {
-        return $this->_error;
+        return $this->error;
     }
 
     /**
@@ -204,7 +195,7 @@ class Email
      */
     protected function setError($error)
     {
-        $this->_error = $error;
+        $this->error = $error;
     }
 
     /**
@@ -217,11 +208,18 @@ class Email
         if (in_array($this->options['mail_type'], [1, 2])) {
             try {
                 $result = $this->mail->send();
-            } catch (\PHPMailer\PHPMailer\Exception $e) {
+            } catch (SendException $e) {
+                $this->setError($e->getCode() . $e->getMessage());
+            } catch (CodeException $e) {
+                preg_match_all("/Expected: (\d+)\, Got: (\d+)( \| (.*))?\$/i", $e->getMessage(), $matches);
+                $code = isset($matches[2][3]) ? $matches[2][3] : 0;
+                $message = isset($matches[2][0]) ? $matches[4][0] : $e->getMessage();
+                $this->setError($message);
+            } catch (\Exception $e) {
                 $this->setError($e->getMessage());
             }
 
-            $this->setError($result ? '' : $this->mail->ErrorInfo);
+            $this->setError($result ? '' : $this->getError());
         } else {
             //邮件功能已关闭
             $this->setError(__('Mail already closed'));

+ 1 - 0
application/common/model/Config.php

@@ -183,6 +183,7 @@ class Config extends Model
             'mimetype'  => $uploadcfg['mimetype'],
             'chunking'  => $uploadcfg['chunking'],
             'chunksize' => $uploadcfg['chunksize'],
+            'savekey'   => $uploadcfg['savekey'],
             'multipart' => [],
             'multiple'  => $uploadcfg['multiple'],
             'storage'   => 'local'

+ 1 - 1
application/extra/upload.php

@@ -21,7 +21,7 @@ return [
     /**
      * 可上传的文件类型
      */
-    'mimetype'  => 'jpg,png,bmp,jpeg,gif,zip,rar,xls,xlsx',
+    'mimetype'  => 'jpg,png,bmp,jpeg,gif,zip,rar,xls,xlsx,wav,mp4,mp3,pdf',
     /**
      * 是否支持批量上传
      */

+ 17 - 2
application/index/controller/User.php

@@ -65,7 +65,7 @@ class User extends Frontend
      */
     public function register()
     {
-        $url = $this->request->request('url', '');
+        $url = $this->request->request('url', '', 'trim');
         if ($this->auth->id) {
             $this->success(__('You\'ve logged in, do not login again'), $url ? $url : url('user/index'));
         }
@@ -144,7 +144,7 @@ class User extends Frontend
      */
     public function login()
     {
-        $url = $this->request->request('url', '');
+        $url = $this->request->request('url', '', 'trim');
         if ($this->auth->id) {
             $this->success(__('You\'ve logged in, do not login again'), $url ? $url : url('user/index'));
         }
@@ -267,6 +267,7 @@ class User extends Frontend
         $this->request->filter(['strip_tags']);
         if ($this->request->isAjax()) {
             $mimetypeQuery = [];
+            $where = [];
             $filter = $this->request->request('filter');
             $filterArr = (array)json_decode($filter, true);
             if (isset($filterArr['mimetype']) && preg_match("/[]\,|\*]/", $filterArr['mimetype'])) {
@@ -281,17 +282,31 @@ class User extends Frontend
                         }
                     }
                 };
+            } elseif (isset($filterArr['mimetype'])) {
+                $where['mimetype'] = ['like', '%' . $filterArr['mimetype'] . '%'];
             }
+
+            if (isset($filterArr['filename'])) {
+                $where['filename'] = ['like', '%' . $filterArr['filename'] . '%'];
+            }
+
+            if (isset($filterArr['createtime'])) {
+                $timeArr = explode(' - ', $filterArr['createtime']);
+                $where['createtime'] = ['between', [strtotime($timeArr[0]), strtotime($timeArr[1])]];
+            }
+
             $model = new Attachment();
             $offset = $this->request->get("offset", 0);
             $limit = $this->request->get("limit", 0);
             $total = $model
+                ->where($where)
                 ->where($mimetypeQuery)
                 ->where('user_id', $this->auth->id)
                 ->order("id", "DESC")
                 ->count();
 
             $list = $model
+                ->where($where)
                 ->where($mimetypeQuery)
                 ->where('user_id', $this->auth->id)
                 ->order("id", "DESC")

+ 1 - 1
bower.json

@@ -27,7 +27,7 @@
     "fastadmin-cxselect": "~1.4.0",
     "fastadmin-dragsort": "~1.0.0",
     "fastadmin-addtabs": "~1.0.5",
-    "fastadmin-selectpage": "~1.0.0",
+    "fastadmin-selectpage": "~1.0.6",
     "fastadmin-layer": "~3.1.2",
     "bootstrap-slider": "*"
   }

+ 3 - 4
composer.json

@@ -15,19 +15,18 @@
         }
     ],
     "require": {
-        "php": ">=7.0.0",
+        "php": ">=7.1.0",
         "topthink/framework": "~5.0.24",
         "topthink/think-captcha": "^1.0",
-        "phpmailer/phpmailer": "~6.1.6",
         "karsonzhang/fastadmin-addons": "~1.2.4",
         "overtrue/pinyin": "~3.0",
         "phpoffice/phpspreadsheet": "^1.2",
         "overtrue/wechat": "4.2.11",
         "nelexa/zip": "^3.3",
-        "symfony/var-exporter": "^4.4.13",
         "ext-json": "*",
         "ext-curl": "*",
-        "ext-pdo": "*"
+        "ext-pdo": "*",
+        "txthinking/mailer": "^2.0"
     },
     "config": {
         "preferred-install": "dist"

+ 20 - 0
public/assets/js/backend/addon.js

@@ -180,6 +180,16 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                     Toastr.success(ret.msg);
                     operate(data.addon.name, 'enable', false);
                     return false;
+                }, function (data, ret) {
+                    if (ret.msg && ret.msg.match(/(login|登录)/g)) {
+                        return Layer.alert(ret.msg, {
+                            title: __('Warning'),
+                            btn: [__('Login now')],
+                            yes: function (index, layero) {
+                                $(".btn-userinfo").trigger("click");
+                            }
+                        });
+                    }
                 });
             });
 
@@ -262,7 +272,17 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
                             return false;
                         },
                         success: function (layero, index) {
+                            this.checkEnterKey = function(event){
+                                if(event.keyCode === 13){
+                                    $(".layui-layer-btn0").trigger("click");
+                                    return false;
+                                }
+                            };
+                            $(document).on('keydown', this.checkEnterKey);
                             $(".layui-layer-btn1", layero).prop("href", "http://www.fastadmin.net/user/register.html").prop("target", "_blank");
+                        },
+                        end: function(){
+                            $(document).off('keydown', this.checkEnterKey);
                         }
                     });
                 } else {

+ 2 - 1
public/assets/js/fast.js

@@ -103,7 +103,8 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
             //获取修复后可访问的cdn链接
             cdnurl: function (url, domain) {
                 var rule = new RegExp("^((?:[a-z]+:)?\\/\\/|data:image\\/)", "i");
-                var url = rule.test(url) ? url : Config.upload.cdnurl + url;
+                var cdnurl = Config.upload.cdnurl;
+                url = rule.test(url) || (cdnurl && url.indexOf(cdnurl) === 0) ? url : cdnurl + url;
                 if (domain && !rule.test(url)) {
                     domain = typeof domain === 'string' ? domain : location.origin;
                     url = domain + url;

+ 7 - 10
public/assets/js/frontend/user.js

@@ -140,25 +140,22 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
                     showExport: false,
                     columns: [
                         [
-                            {field: 'state', checkbox: multiple, visible: multiple, operate:false},
-                            {field: 'id', title: __('Id')},
-                            {field: 'url', title: __('Preview'), formatter: function (value, row, index) {
+                            {field: 'state', checkbox: multiple, visible: multiple, operate: false},
+                            {field: 'id', title: __('Id'), operate: false},
+                            {
+                                field: 'url', title: __('Preview'), formatter: function (value, row, index) {
                                     if (row.mimetype.indexOf("image") > -1) {
                                         var style = row.storage === 'upyun' ? '!/fwfh/120x90' : '';
                                         return '<a href="' + row.fullurl + '" target="_blank"><img src="' + row.fullurl + style + '" alt="" style="max-height:90px;max-width:120px"></a>';
                                     } else {
                                         return '<a href="' + row.fullurl + '" target="_blank"><img src="' + Fast.api.fixurl("ajax/icon") + "?suffix=" + row.imagetype + '" alt="" style="max-height:90px;max-width:120px"></a>';
                                     }
-                                }, operate: false},
+                                }, operate: false
+                            },
                             {field: 'filename', title: __('Filename'), formatter: Table.api.formatter.search, operate: 'like'},
                             {field: 'imagewidth', title: __('Imagewidth'), operate: false},
                             {field: 'imageheight', title: __('Imageheight'), operate: false},
-                            {
-                                field: 'mimetype', title: __('Mimetype'), operate: 'LIKE %...%',
-                                process: function (value, arg) {
-                                    return value.replace(/\*/g, '%');
-                                }
-                            },
+                            {field: 'mimetype', title: __('Mimetype'), formatter: Table.api.formatter.search},
                             {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
                             {
                                 field: 'operate', title: __('Operate'), events: {

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

@@ -121,7 +121,7 @@ require.config({
             'css': '../libs/require-css/css.min'
         }
     },
-    waitSeconds: 30,
+    waitSeconds: 60,
     charset: 'utf-8' // 文件编码
 });
 

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

@@ -120,7 +120,7 @@ require.config({
             'css': '../libs/require-css/css.min'
         }
     },
-    waitSeconds: 30,
+    waitSeconds: 60,
     charset: 'utf-8' // 文件编码
 });
 

+ 2 - 1
public/assets/js/require-upload.js

@@ -141,7 +141,7 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
                         //上传URL
                         url = url ? url : Config.upload.uploadurl;
                         url = Fast.api.fixurl(url);
-                        var chunking = false, chunkSize = Config.upload.chunksize || 2097152;
+                        var chunking = false, chunkSize = Config.upload.chunksize || 2097152, timeout = Config.upload.timeout || 600000;
 
                         //最大可上传文件大小
                         maxsize = typeof maxsize !== "undefined" ? maxsize : Config.upload.maxsize;
@@ -198,6 +198,7 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
                             maxFilesize: maxFilesize,
                             acceptedFiles: mimetype,
                             maxFiles: (maxcount && parseInt(maxcount) > 1 ? maxcount : (multiple ? null : 1)),
+                            timeout: timeout,
                             previewsContainer: false,
                             dictDefaultMessage: __("Drop files here to upload"),
                             dictFallbackMessage: __("Your browser does not support drag'n'drop file uploads"),