mirror of https://gitee.com/karson/fastadmin.git
parent
bdde403060
commit
04af358649
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1 +1,24 @@
|
|||
{if $Think.config.fastadmin.user_api_captcha}
|
||||
<script type="text/html" id="captchatpl">
|
||||
<div class="p-4 form-section">
|
||||
<form name="captcha-form" class="form-vertical" action="" method="post">
|
||||
<div class="form-group mb-4">
|
||||
<label class="control-label hidden">{:__('Captcha')}</label>
|
||||
<div class="controls">
|
||||
<div class="input-group">
|
||||
<input type="text" name="captcha" class="form-control" data-rule="required;length({$Think.config.captcha.length})" />
|
||||
<span class="input-group-btn" style="padding:0;border:none;">
|
||||
<img src="{:captcha_src()}" width="107" height="32" class="captcha-img" onclick="this.src = '{:captcha_src()}?r=' + Math.random();"/>
|
||||
</span>
|
||||
</div>
|
||||
<span class="msg-box n-right" style="left:0;top:33px;text-align: left;" for="captcha"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block" type="submit">{:__('Submit')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</script>
|
||||
{/if}
|
||||
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version|htmlentities}"></script>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,45 @@
|
|||
<div id="content-container" class="container">
|
||||
<div class="user-section login-section">
|
||||
<div class="logon-tab clearfix"><a class="active">{:__('Sign in')}</a> <a href="{:url('user/register')}?url={$url|urlencode|htmlentities}">{:__('Sign up')}</a></div>
|
||||
<div class="logon-tab clearfix">
|
||||
<a class="active">{:__('Sign in')}</a>
|
||||
{if config('fastadmin.user_register')}
|
||||
<a href="{:url('user/register')}?url={$url|urlencode|htmlentities}">{:__('Sign up')}</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="login-main">
|
||||
<form name="form" id="login-form" class="form-vertical" method="POST" action="">
|
||||
|
||||
<form name="form" id="login-form" class="form-vertical" method="POST" action="{$loginAction}">
|
||||
<!--@IndexLoginFormBegin-->
|
||||
<input type="hidden" name="url" value="{$url|htmlentities}"/>
|
||||
{:token()}
|
||||
<div class="form-group">
|
||||
<div class="form-group {:$loginType==='mobile'?'':'hidden'}" data-login="mobile">
|
||||
<label class="control-label">{:__('Mobile')}</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="mobile" name="mobile" value="13211111111" data-rule="required;mobile" class="form-control" placeholder="{:__('Please enter your mobile phone number')}">
|
||||
<p class="help-block"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group {:$loginType==='mobile'?'':'hidden'}" data-login="mobile">
|
||||
<label class="control-label">{:__('Captcha')}</label>
|
||||
<div class="controls">
|
||||
<div class="input-group">
|
||||
<input type="text" name="captcha" class="form-control" placeholder="{:__('Please enter %s numbers', $Think.config.captcha.length)}" maxlength="{$Think.config.captcha.length}" data-rule="required;length({$Think.config.captcha.length});integer[+]"/>
|
||||
<span class="input-group-btn" style="padding:0;border:none;">
|
||||
<a href="javascript:;" class="btn btn-info btn-captcha" data-url="{:url('api/sms/send')}" data-id="login" data-type="mobile" data-event="mobilelogin">{:__('Send verification code')}</a>
|
||||
</span>
|
||||
</div>
|
||||
<p class="help-block"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group {:$loginType==='account'?'':'hidden'}" data-login="account">
|
||||
<label class="control-label" for="account">{:__('Account')}</label>
|
||||
<div class="controls">
|
||||
<input class="form-control" id="account" type="text" name="account" value="" data-rule="required" placeholder="{:__('Email/Mobile/Username')}" autocomplete="off">
|
||||
<div class="help-block"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group {:$loginType==='account'?'':'hidden'}" data-login="account">
|
||||
<label class="control-label" for="password">{:__('Password')}</label>
|
||||
<div class="controls">
|
||||
<input class="form-control" id="password" type="password" name="password" data-rule="required;password" placeholder="{:__('Password')}" autocomplete="off">
|
||||
|
|
@ -26,12 +52,25 @@
|
|||
<input type="checkbox" name="keeplogin" checked="checked" value="1"> {:__('Keep login')}
|
||||
</label>
|
||||
</div>
|
||||
<div class="pull-right"><a href="javascript:;" class="btn-forgot">{:__('Forgot password')}</a></div>
|
||||
<div class="pull-right {:$loginType==='account'?'':'hidden'}" data-login="account"><a href="javascript:;" class="btn-forgot">{:__('Forgot password')}</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-block">{:__('Sign in')}</button>
|
||||
<a href="{:url('user/register')}?url={$url|urlencode|htmlentities}" class="btn btn-default btn-lg btn-block mt-3 no-border">{:__("Don't have an account? Sign up")}</a>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 text-left py-2">
|
||||
<a href="javascript:" class="btn-switchlogin" data-type="{$loginType}"
|
||||
data-account-action="{:url('user/login')}"
|
||||
data-mobile-action="{:url('user/mobilelogin')}"
|
||||
data-account-text='{:__("Sign in with account")}'
|
||||
data-mobile-text='{:__("Sign in with mobile phone")}'>{:$loginType==='mobile'?__("Sign in with account"):__("Sign in with mobile phone")}</a>
|
||||
</div>
|
||||
{if config('fastadmin.user_register')}
|
||||
<div class="col-xs-6 text-right py-2">
|
||||
<a href="{:url('user/register')}?url={$url|urlencode|htmlentities}">{:__("Don\'t have an account? Sign up")}</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<!--@IndexLoginFormEnd-->
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
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: "<img src='" + data.image + "' width='400' height='400' /><div class='text-center panel-title'>扫一扫关注公众号获取验证码</div>", type: 1});
|
||||
Layer.open({
|
||||
title: false,
|
||||
area: [Math.min($(window).width(), 400) + "px", "430px"],
|
||||
offset: "130px",
|
||||
content: "<img src='" + data.image + "' width='400' height='400' /><div class='text-center panel-title'>扫一扫关注公众号获取验证码</div>",
|
||||
type: 1
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
//本地验证未通过时提示
|
||||
|
|
|
|||
Loading…
Reference in New Issue