diff --git a/application/config.php b/application/config.php index 062bf917..a45dcf54 100755 --- a/application/config.php +++ b/application/config.php @@ -271,7 +271,11 @@ return [ 'usercenter' => true, //会员注册验证码类型email/mobile/wechat/text/false 'user_register_captcha' => 'text', - //会员主页URL规则 + //是否启用发送前验证码(用于短信和邮件发送) + 'user_api_captcha' => false, + //会员登录默认类型,支持mobile和account + 'user_login_type' => 'account', + //会员主页URL规则,{uid}表示用户的ID 'user_home_url' => '/u/{uid}', //是否启用会员字母头像 'user_letter_avatar' => true, diff --git a/application/index/controller/User.php b/application/index/controller/User.php index d265c852..ec9d3deb 100644 --- a/application/index/controller/User.php +++ b/application/index/controller/User.php @@ -7,6 +7,7 @@ use app\common\controller\Frontend; use app\common\library\Ems; use app\common\library\Sms; use app\common\model\Attachment; +use fast\Random; use think\Config; use think\Cookie; use think\Hook; @@ -19,7 +20,7 @@ use think\Validate; class User extends Frontend { protected $layout = 'default'; - protected $noNeedLogin = ['login', 'register', 'third']; + protected $noNeedLogin = ['login', 'mobilelogin', 'register', 'third']; protected $noNeedRight = ['*']; public function _initialize() @@ -122,7 +123,8 @@ class User extends Frontend $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); } if ($this->auth->register($username, $password, $email, $mobile)) { - $this->success(__('Sign up successful'), $url ? $url : url('user/index')); + $this->auth->getUser()->save(['verification' => ['email' => $captchaType == 'email' ? 1 : 0, 'mobile' => $captchaType == 'mobile' ? 1 : 0]]); + $this->success(__('Sign up successful'), $url ?: url('user/index')); } else { $this->error($this->auth->getError(), null, ['token' => $this->request->token()]); } @@ -132,6 +134,7 @@ class User extends Frontend if (!$url && $referer && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) { $url = $referer; } + $this->view->assign('captchaType', config('fastadmin.user_register_captcha')); $this->view->assign('url', $url); $this->view->assign('title', __('Register')); @@ -175,7 +178,7 @@ class User extends Frontend $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); } if ($this->auth->login($account, $password)) { - $this->success(__('Logged in successful'), $url ? $url : url('user/index')); + $this->success(__('Logged in successful'), $url ?: url('user/index'), '', 0); } else { $this->error($this->auth->getError(), null, ['token' => $this->request->token()]); } @@ -185,6 +188,57 @@ class User extends Frontend if (!$url && $referer && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) { $url = $referer; } + $this->view->assign('loginType', config('fastadmin.user_login_type') ?? 'mobile'); + $this->view->assign('loginAction', config('fastadmin.user_login_type') === 'mobile' ? url('user/mobilelogin') : url('user/login')); + $this->view->assign('url', $url); + $this->view->assign('title', __('Login')); + return $this->view->fetch(); + } + + /** + * 手机号验证码登录 + */ + public function mobilelogin() + { + $url = $this->request->request('url', '', 'url_clean'); + if ($this->request->isPost()) { + $mobile = $this->request->post('mobile'); + $captcha = $this->request->post('smscode', $this->request->post('captcha')); + if (!$mobile || !$captcha) { + $this->error(__('Invalid parameters')); + } + if (!Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + if (!Sms::check($mobile, $captcha, 'mobilelogin')) { + $this->error(__('Captcha is incorrect')); + } + $user = \app\common\model\User::getByMobile($mobile); + if ($user) { + if ($user->status != 'normal') { + $this->error(__('Account is locked')); + } + //如果已经有账号则直接登录 + $ret = $this->auth->direct($user->id); + } else { + $ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []); + //如果是手机号首次注册则直接设定为已验证 + $this->auth->getUser()->save(['verification' => ['email' => 0, 'mobile' => 1]]); + } + if ($ret) { + Sms::flush($mobile, 'mobilelogin'); + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Logged in successful'), $url); + } else { + $this->error($this->auth->getError()); + } + } + //判断来源 + $referer = $this->request->server('HTTP_REFERER'); + if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host())) + && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) { + $url = $referer; + } $this->view->assign('url', $url); $this->view->assign('title', __('Login')); return $this->view->fetch(); diff --git a/application/index/view/common/script.html b/application/index/view/common/script.html index 1eb71b0e..80ac3cfa 100644 --- a/application/index/view/common/script.html +++ b/application/index/view/common/script.html @@ -1 +1,24 @@ +{if $Think.config.fastadmin.user_api_captcha} + +{/if} diff --git a/application/index/view/user/login.html b/application/index/view/user/login.html index 6b521925..e4a97165 100755 --- a/application/index/view/user/login.html +++ b/application/index/view/user/login.html @@ -1,19 +1,45 @@
- +
+ {:__('Sign in')} + {if config('fastadmin.user_register')} + {:__('Sign up')} + {/if} +
- + \ No newline at end of file diff --git a/public/assets/js/fast.js b/public/assets/js/fast.js index 63dc4d41..f8dc9fc0 100644 --- a/public/assets/js/fast.js +++ b/public/assets/js/fast.js @@ -58,6 +58,67 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine } }, api: { + //发送验证码 + sendcaptcha: function (btn, type, data, success, error) { + $(btn).addClass("disabled", true).text("发送中..."); + var si = {}; + Frontend.api.ajax({url: $(btn).data("url"), data: data}, function (data, ret) { + clearInterval(si[type]); + var seconds = 60; + si[type] = setInterval(function () { + seconds--; + if (seconds <= 0) { + clearInterval(si); + $(btn).removeClass("disabled").text("发送验证码"); + } else { + $(btn).addClass("disabled").text(seconds + "秒后可再次发送"); + } + }, 1000); + if (typeof success == 'function') { + success.call(this, data, ret); + } + }, function (data, ret) { + $(btn).removeClass("disabled").text('发送验证码'); + + if (typeof error == 'function') { + error.call(this, data, ret); + } + }); + }, + //准备验证码 + preparecaptcha: function (btn, type, data) { + require(['form'], function (Form) { + Layer.open({ + title: '请完成验证', + type: 1, + content: Template("captchatpl", {}), + offset: "130px", + btnAlign: 'c', + success: function (layero, index) { + var form = $("form", layero); + $("input[name=captcha]", form).focus(); + form.data("validator-options", { + valid: function (ret) { + data.captcha = $("input[name=captcha]", form).val(); + if(data.captcha.length < 4){ + Toastr.error("验证码不正确"); + $("input[name=captcha]", form).focus(); + return false; + } + Frontend.api.sendcaptcha(btn, type, data, function (data, ret) { + Layer.close(index); + $(btn).closest("form").find("input[name='captcha']").focus(); + }, function (data, ret) { + $("img.captcha-img", form).trigger("click"); + }); + return true; + } + }) + Form.api.bindevent(form); + } + }); + }); + }, //发送Ajax请求 ajax: function (options, success, error) { options = typeof options === 'string' ? {url: options} : options; diff --git a/public/assets/js/frontend.js b/public/assets/js/frontend.js index 0bd60fd0..b0a4a08b 100644 --- a/public/assets/js/frontend.js +++ b/public/assets/js/frontend.js @@ -7,28 +7,6 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) { $(document).on("click", ".btn-captcha", function (e) { var type = $(this).data("type") ? $(this).data("type") : 'mobile'; var btn = this; - Frontend.api.sendcaptcha = function (btn, type, data, callback) { - $(btn).addClass("disabled", true).text("发送中..."); - - Frontend.api.ajax({url: $(btn).data("url"), data: data}, function (data, ret) { - clearInterval(si[type]); - var seconds = 60; - si[type] = setInterval(function () { - seconds--; - if (seconds <= 0) { - clearInterval(si); - $(btn).removeClass("disabled").text("发送验证码"); - } else { - $(btn).addClass("disabled").text(seconds + "秒后可再次发送"); - } - }, 1000); - if (typeof callback == 'function') { - callback.call(this, data, ret); - } - }, function () { - $(btn).removeClass("disabled").text('发送验证码'); - }); - }; if (['mobile', 'email'].indexOf(type) > -1) { var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form")); var text = type === 'email' ? '邮箱' : '手机号码'; @@ -47,9 +25,13 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) { } element.isValid(function (v) { if (v) { - var data = {event: $(btn).data("event")}; + var data = {event: $(btn).data("event"), id: $(btn).data("id")}; data[type] = element.val(); - Frontend.api.sendcaptcha(btn, type, data); + if ($("#captchatpl").length === 0) { + Frontend.api.sendcaptcha(btn, type, data); + } else { + Frontend.api.preparecaptcha(btn, type, data); + } } else { Layer.msg("请确认已经输入了正确的" + text + "!"); } @@ -57,7 +39,13 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) { } else { var data = {event: $(btn).data("event")}; Frontend.api.sendcaptcha(btn, type, data, function (data, ret) { - Layer.open({title: false, area: ["400px", "430px"], content: "
扫一扫关注公众号获取验证码
", type: 1}); + Layer.open({ + title: false, + area: [Math.min($(window).width(), 400) + "px", "430px"], + offset: "130px", + content: "
扫一扫关注公众号获取验证码
", + type: 1 + }); }); } return false; diff --git a/public/assets/js/frontend/user.js b/public/assets/js/frontend/user.js index cfbfaa60..5a979ea7 100755 --- a/public/assets/js/frontend/user.js +++ b/public/assets/js/frontend/user.js @@ -4,7 +4,8 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und $.each(errors, function (i, j) { Layer.msg(j); }); - } + }, + ignore: ':hidden' }; var Controller = { login: function () { @@ -26,6 +27,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und Layer.open({ type: 1, title: __('Reset password'), + offset: "130px", area: [$(window).width() < 450 ? ($(window).width() - 10) + "px" : "450px", "355px"], content: content, success: function (layero) { @@ -45,6 +47,18 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und } }); }); + + //切换账号手机验证码登录 + $(document).on("click", ".btn-switchlogin", function () { + var form = $(this).closest("form"); + var current = $(this).data("type"); + var type = current == 'mobile' ? 'account' : 'mobile'; + var text = $(this).data(current + "-text"); + $(this).text(text).data("type", type); + $("[data-login]").addClass("hidden"); + $("[data-login='" + type + "']").removeClass("hidden"); + form.attr("action", $(this).data(type + "-action")); + }); }, register: function () { //本地验证未通过时提示