作者 Karson

新增邮箱验证码功能

新增Token默认有效期
新增Token检测和刷新API接口
新增前台发送验证码事件监听

修复修改个人资料时跳转的BUG
修复API文档Token设置无效的BUG
修复后台移除左侧链接后菜单消失的BUG

优化前台样式CSS,精简多余的CSS
优化会员登录注册视图模板
... ... @@ -413,7 +413,7 @@
var token = $('#token').val();
if (token.length > 0) {
headers[token] = token;
headers['token'] = token;
}
$("#sandbox" + theId + " .headers input[type=text]").each(function () {
... ...
... ... @@ -338,6 +338,21 @@ INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'stri
COMMIT;
-- ----------------------------
-- Table structure for fa_ems
-- ----------------------------
DROP TABLE IF EXISTS `fa_ems`;
CREATE TABLE `fa_ems` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
`event` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '事件',
`email` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '邮箱',
`code` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '验证码',
`times` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '验证次数',
`ip` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'IP',
`createtime` int(10) UNSIGNED NULL DEFAULT 0 COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='邮箱验证码表';
-- ----------------------------
-- Table structure for fa_sms
-- ----------------------------
DROP TABLE IF EXISTS `fa_sms`;
... ... @@ -412,8 +427,8 @@ CREATE TABLE `fa_user` (
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
`level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '等级',
`gender` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '性别',
`birthday` date NOT NULL COMMENT '生日',
`bio` varchar(100) NOT NULL COMMENT '格言',
`birthday` date COMMENT '生日',
`bio` varchar(100) NOT NULL DEFAULT '' COMMENT '格言',
`score` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '积分',
`successions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '连续登录天数',
`maxsuccessions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '最大连续登录天数',
... ...
<?php
namespace app\api\controller;
use app\common\controller\Api;
use app\common\library\Ems as Emslib;
use app\common\model\User;
/**
* 邮箱验证码接口
*/
class Ems extends Api
{
protected $noNeedLogin = '*';
protected $noNeedRight = '*';
public function _initialize()
{
parent::_initialize();
\think\Hook::add('ems_send', function($params) {
$obj = \app\common\library\Email::instance();
$result = $obj
->to($params->email)
->subject('验证码')
->message("你的验证码是:" . $params->code)
->send();
return $result;
});
}
/**
* 发送验证码
*
* @param string $email 邮箱
* @param string $event 事件名称
*/
public function send()
{
$email = $this->request->request("email");
$event = $this->request->request("event");
$event = $event ? $event : 'register';
$last = Emslib::get($email, $event);
if ($last && time() - $last['createtime'] < 60)
{
$this->error(__('发送频繁'));
}
if ($event)
{
$userinfo = User::getByEmail($email);
if ($event == 'register' && $userinfo)
{
//已被注册
$this->error(__('已被注册'));
}
else if (in_array($event, ['changeemail']) && $userinfo)
{
//被占用
$this->error(__('已被占用'));
}
else if (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo)
{
//未注册
$this->error(__('未注册'));
}
}
$ret = Emslib::send($email, NULL, $event);
if ($ret)
{
$this->success(__('发送成功'));
}
else
{
$this->error(__('发送失败'));
}
}
/**
* 检测验证码
*
* @param string $email 邮箱
* @param string $event 事件名称
* @param string $captcha 验证码
*/
public function check()
{
$email = $this->request->request("email");
$event = $this->request->request("event");
$event = $event ? $event : 'register';
$captcha = $this->request->request("captcha");
if ($event)
{
$userinfo = User::getByEmail($email);
if ($event == 'register' && $userinfo)
{
//已被注册
$this->error(__('已被注册'));
}
else if (in_array($event, ['changeemail']) && $userinfo)
{
//被占用
$this->error(__('已被占用'));
}
else if (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo)
{
//未注册
$this->error(__('未注册'));
}
}
$ret = Emslib::check($email, $captcha, $event);
if ($ret)
{
$this->success(__('成功'));
}
else
{
$this->error(__('验证码不正确'));
}
}
}
... ...
... ... @@ -7,7 +7,7 @@ use app\common\library\Sms as Smslib;
use app\common\model\User;
/**
* 短信接口
* 手机短信接口
*/
class Sms extends Api
{
... ...
<?php
namespace app\api\controller;
use app\common\controller\Api;
/**
* Token接口
*/
class Token extends Api
{
protected $noNeedLogin = [];
protected $noNeedRight = '*';
public function _initialize()
{
parent::_initialize();
}
/**
* 检测Token是否过期
*
*/
public function check()
{
$token = $this->auth->getToken();
$tokenInfo = \app\common\library\Token::get($token);
$this->success('', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]);
}
/**
* 刷新Token
*
*/
public function refresh()
{
$token = $this->auth->getToken();
$tokenInfo = \app\common\library\Token::get($token);
$tokenInfo->expiretime = time() + 2592000;
$tokenInfo->save();
$this->success('', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]);
}
}
... ...
... ... @@ -3,7 +3,7 @@
namespace app\api\controller;
use app\common\controller\Api;
use app\common\library\Email;
use app\common\library\Ems;
use app\common\library\Sms;
use fast\Random;
use think\Validate;
... ... @@ -179,12 +179,14 @@ class User extends Api
* 修改邮箱
*
* @param string $email 邮箱
* @param string $captcha 验证码
*/
public function changeemail()
{
$user = $this->auth->getUser();
$email = $this->request->post('email');
if (!$email)
$captcha = $this->request->request('captcha');
if (!$email || !$captcha)
{
$this->error(__('Invalid parameters'));
}
... ... @@ -196,17 +198,18 @@ class User extends Api
{
$this->error(__('Email already exists'));
}
$result = Ems::check($email, $captcha, 'changeemail');
if (!$result)
{
$this->error(__('Captcha is incorrect'));
}
$verification = $user->verification;
$verification->email = 0;
$verification->email = 1;
$user->verification = $verification;
$user->email = $email;
$user->save();
$time = time();
$code = ['id' => $user->id, 'time' => $time, 'key' => md5(md5($user->id . $user->email . $time) . $user->salt)];
$code = base64_encode(http_build_query($code));
$url = url("index/user/activeemail", ['code' => $code], true, true);
$message = __('Verify email') . ":<a href='{$url}'>{$url}</a>";
Email::instance()->to($email)->subject(__('Verify email'))->message($message)->send();
Ems::flush($email, 'changeemail');
$this->success();
}
... ... @@ -291,29 +294,51 @@ class User extends Api
*/
public function resetpwd()
{
$type = $this->request->request("type");
$mobile = $this->request->request("mobile");
$email = $this->request->request("email");
$newpassword = $this->request->request("newpassword");
$captcha = $this->request->request("captcha");
if (!$mobile || !$newpassword || !$captcha)
if (!$newpassword || !$captcha)
{
$this->error(__('Invalid parameters'));
}
if ($mobile && !Validate::regex($mobile, "^1\d{10}$"))
if ($type == 'mobile')
{
$this->error(__('Mobile is incorrect'));
}
$user = \app\common\model\User::getByMobile($mobile);
if (!$user)
{
$this->error(__('User not found'));
if (!Validate::regex($mobile, "^1\d{10}$"))
{
$this->error(__('Mobile is incorrect'));
}
$user = \app\common\model\User::getByMobile($mobile);
if (!$user)
{
$this->error(__('User not found'));
}
$ret = Sms::check($mobile, $captcha, 'resetpwd');
if (!$ret)
{
$this->error(__('Captcha is incorrect'));
}
Sms::flush($mobile, 'resetpwd');
}
$ret = Sms::check($mobile, $captcha, 'resetpwd');
if (!$ret)
else
{
$this->error(__('Captcha is incorrect'));
if (!Validate::is($email, "email"))
{
$this->error(__('Email is incorrect'));
}
$user = \app\common\model\User::getByEmail($email);
if (!$user)
{
$this->error(__('User not found'));
}
$ret = Ems::check($email, $captcha, 'resetpwd');
if (!$ret)
{
$this->error(__('Captcha is incorrect'));
}
Ems::flush($email, 'resetpwd');
}
Sms::flush($mobile, 'resetpwd');
//模拟一次登录
$this->auth->direct($user->id);
$ret = $this->auth->changepwd($newpassword, '', true);
... ...
... ... @@ -24,7 +24,7 @@ class Validate extends Api
* 检测邮箱
*
* @param string $email 邮箱
* @param string $id 会员ID
* @param string $id 排除会员ID
*/
public function check_email_available()
{
... ... @@ -42,7 +42,7 @@ class Validate extends Api
* 检测用户名
*
* @param string $username 用户名
* @param string $id 会员ID
* @param string $id 排除会员ID
*/
public function check_username_available()
{
... ... @@ -60,16 +60,16 @@ class Validate extends Api
* 检测手机
*
* @param string $mobile 手机号
* @param string $id 会员ID
* @param string $id 排除会员ID
*/
public function check_mobile_available()
{
$email = $this->request->request('mobile');
$mobile = $this->request->request('mobile');
$id = (int) $this->request->request('id');
$count = User::where('mobile', '=', $email)->where('id', '<>', $id)->count();
$count = User::where('mobile', '=', $mobile)->where('id', '<>', $id)->count();
if ($count > 0)
{
$this->error(__('已经使用该手机号注册'));
$this->error(__('该手机号已经占用'));
}
$this->success();
}
... ... @@ -81,8 +81,8 @@ class Validate extends Api
*/
public function check_mobile_exist()
{
$email = $this->request->request('mobile');
$count = User::where('mobile', '=', $email)->count();
$mobile = $this->request->request('mobile');
$count = User::where('mobile', '=', $mobile)->count();
if (!$count)
{
$this->error(__('手机号不存在'));
... ... @@ -91,11 +91,27 @@ class Validate extends Api
}
/**
* 检测验证码
* 检测邮箱
*
* @param string $mobile 手机号
* @param string $captcha 验证码
* @param string $event 事件
* @param string $mobile 邮箱
*/
public function check_email_exist()
{
$email = $this->request->request('email');
$count = User::where('email', '=', $email)->count();
if (!$count)
{
$this->error(__('邮箱不存在'));
}
$this->success();
}
/**
* 检测手机验证码
*
* @param string $mobile 手机号
* @param string $captcha 验证码
* @param string $event 事件
*/
public function check_sms_correct()
{
... ... @@ -109,4 +125,23 @@ class Validate extends Api
$this->success();
}
/**
* 检测邮箱验证码
*
* @param string $email 邮箱
* @param string $captcha 验证码
* @param string $event 事件
*/
public function check_ems_correct()
{
$email = $this->request->request('email');
$captcha = $this->request->request('captcha');
$event = $this->request->request('event');
if (!\app\common\library\Ems::check($email, $captcha, $event))
{
$this->error(__('验证码不正确'));
}
$this->success();
}
}
... ...
... ... @@ -19,7 +19,8 @@ class Auth
protected $_logined = FALSE;
protected $_user = NULL;
protected $_token = '';
protected $keeptime = 0;
//Token默认有效时长
protected $keeptime = 2592000;
protected $requestUri = '';
protected $rules = [];
//默认配置
... ... @@ -203,7 +204,7 @@ class Auth
//设置Token
$this->_token = Random::uuid();
Token::set($this->_token, $user->id);
Token::set($this->_token, $user->id, $this->keeptime);
//注册成功的事件
Hook::listen("user_register_successed", $this->_user);
... ... @@ -349,7 +350,7 @@ class Auth
$this->_user = $user;
$this->_token = Random::uuid();
Token::set($this->_token, $user->id);
Token::set($this->_token, $user->id, $this->keeptime);
$this->_logined = TRUE;
... ...
<?php
namespace app\common\library;
use think\Hook;
/**
* 邮箱验证码类
*/
class Ems
{
/**
* 验证码有效时长
* @var int
*/
protected static $expire = 120;
/**
* 最大允许检测的次数
* @var int
*/
protected static $maxCheckNums = 10;
/**
* 获取最后一次邮箱发送的数据
*
* @param int $email 邮箱
* @param string $event 事件
* @return Ems
*/
public static function get($email, $event = 'default')
{
$ems = \app\common\model\Ems::
where(['email' => $email, 'event' => $event])
->order('id', 'DESC')
->find();
Hook::listen('ems_get', $ems, null, true);
return $ems ? $ems : NULL;
}
/**
* 发送验证码
*
* @param int $email 邮箱
* @param int $code 验证码,为空时将自动生成4位数字
* @param string $event 事件
* @return boolean
*/
public static function send($email, $code = NULL, $event = 'default')
{
$code = is_null($code) ? mt_rand(1000, 9999) : $code;
$time = time();
$ip = request()->ip();
$ems = \app\common\model\Ems::create(['event' => $event, 'email' => $email, 'code' => $code, 'ip' => $ip, 'createtime' => $time]);
$result = Hook::listen('ems_send', $ems, null, true);
if (!$result)
{
$ems->delete();
return FALSE;
}
return TRUE;
}
/**
* 发送通知
*
* @param mixed $email 邮箱,多个以,分隔
* @param string $msg 消息内容
* @param string $template 消息模板
* @return boolean
*/
public static function notice($email, $msg = '', $template = NULL)
{
$params = [
'email' => $email,
'msg' => $msg,
'template' => $template
];
$result = Hook::listen('ems_notice', $params, null, true);
return $result ? TRUE : FALSE;
}
/**
* 校验验证码
*
* @param int $email 邮箱
* @param int $code 验证码
* @param string $event 事件
* @return boolean
*/
public static function check($email, $code, $event = 'default')
{
$time = time() - self::$expire;
$ems = \app\common\model\Ems::where(['email' => $email, 'event' => $event])
->order('id', 'DESC')
->find();
if ($ems)
{
if ($ems['createtime'] > $time && $ems['times'] <= self::$maxCheckNums)
{
$correct = $code == $ems['code'];
if (!$correct)
{
$ems->times = $ems->times + 1;
$ems->save();
return FALSE;
}
else
{
$result = Hook::listen('ems_check', $ems, null, true);
return TRUE;
}
}
else
{
// 过期则清空该邮箱验证码
self::flush($email, $event);
return FALSE;
}
}
else
{
return FALSE;
}
}
/**
* 清空指定邮箱验证码
*
* @param int $email 邮箱
* @param string $event 事件
* @return boolean
*/
public static function flush($email, $event = 'default')
{
\app\common\model\Ems::
where(['email' => $email, 'event' => $event])
->delete();
Hook::listen('ems_flush');
return TRUE;
}
}
... ...
... ... @@ -5,7 +5,7 @@ namespace app\common\library;
use think\Hook;
/**
* 验证码类
* 短信验证码类
*/
class Sms
{
... ... @@ -51,7 +51,8 @@ class Sms
{
$code = is_null($code) ? mt_rand(1000, 9999) : $code;
$time = time();
$sms = \app\common\model\Sms::create(['event' => $event, 'mobile' => $mobile, 'code' => $code, 'createtime' => $time]);
$ip = request()->ip();
$sms = \app\common\model\Sms::create(['event' => $event, 'mobile' => $mobile, 'code' => $code, 'ip' => $ip, 'createtime' => $time]);
$result = Hook::listen('sms_send', $sms, null, true);
if (!$result)
{
... ...
<?php
namespace app\common\model;
use think\Model;
/**
* 邮箱验证码
*/
class Ems Extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = false;
// 追加属性
protected $append = [
];
}
... ...
... ... @@ -21,6 +21,16 @@ class Token Extends Model
protected $pk = 'token';
// 追加属性
protected $append = [
'expires_in'
];
/**
* 获取Token剩余有效期
* @return int
*/
public function getExpiresInAttr($value, $data)
{
return $data['expiretime'] ? max(0, $data['expiretime'] - time()) : 365 * 86400;
}
}
... ...
... ... @@ -253,7 +253,7 @@ return [
//自动检测更新
'checkupdate' => false,
//版本号
'version' => '1.0.0.20180308_beta',
'version' => '1.0.0.20180310_beta',
'api_url' => 'http://api.fastadmin.net',
],
];
... ...
... ... @@ -31,8 +31,9 @@ class User extends Frontend
//监听注册登录注销的事件
Hook::add('user_login_successed', function($user) use($auth) {
Cookie::set('uid', $user->id);
Cookie::set('token', $auth->getToken());
$expire = input('post.keeplogin') ? 30 * 86400 : 0;
Cookie::set('uid', $user->id, $expire);
Cookie::set('token', $auth->getToken(), $expire);
});
Hook::add('user_register_successed', function($user) use($auth) {
Cookie::set('uid', $user->id);
... ... @@ -139,7 +140,7 @@ class User extends Frontend
{
$account = $this->request->post('account');
$password = $this->request->post('password');
$keeptime = (int) $this->request->post('keeptime');
$keeplogin = (int) $this->request->post('keeplogin');
$token = $this->request->post('__token__');
$rule = [
'account' => 'require|length:3,50',
... ... @@ -165,7 +166,7 @@ class User extends Frontend
$this->error(__($validate->getError()));
return FALSE;
}
if ($this->auth->login($account, $password, $keeptime))
if ($this->auth->login($account, $password))
{
$synchtml = '';
////////////////同步到Ucenter////////////////
... ... @@ -263,39 +264,6 @@ class User extends Frontend
}
/**
* 激活邮箱
*/
public function activeemail()
{
$code = $this->request->request('code');
$code = base64_decode($code);
parse_str($code, $params);
if (!isset($params['id']) || !isset($params['time']) || !isset($params['key']))
{
$this->error(__('Invalid parameters'));
}
$user = \app\common\model\User::get($params['id']);
if (!$user)
{
$this->error(__('User not found'));
}
if ($user->verification->email)
{
$this->error(__('Email already activation'));
}
if ($key !== md5(md5($user->id . $user->email . $time) . $user->salt) || time() - $params['time'] > 1800)
{
$this->error(__('Secrity code already invalid'));
}
$verification = $user->verification;
$verification->email = 1;
$user->verification = $verification;
$user->save();
$this->success(__('Active email successful'), url('user/index'));
return;
}
/**
* 修改密码
*/
public function changepwd()
... ...
... ... @@ -22,7 +22,8 @@ return [
'Sign up successful' => '注册成功',
'Email active successful' => '邮箱激活成功',
'Username can not be empty' => '用户名不能为空',
'Username must be 6 to 30 characters' => '用户名必须6-30个字符',
'Username must be 3 to 30 characters' => '用户名必须3-30个字符',
'Account must be 3 to 50 characters' => '账户必须3-50个字符',
'Password can not be empty' => '密码不能为空',
'Password must be 6 to 30 characters' => '密码必须6-30个字符',
'Email is incorrect' => '邮箱格式不正确',
... ...
... ... @@ -65,8 +65,9 @@
<!-- /.container -->
</nav>
{__CONTENT__}
<main class="content">
{__CONTENT__}
</main>
<footer>
<div class="container">
... ...
... ... @@ -54,11 +54,12 @@
</div>
</nav>
{__CONTENT__}
<main class="content">
{__CONTENT__}
</main>
<footer class="footer" style="clear:both">
<p class="address">Copyright&nbsp;©&nbsp;2017-2018 fastadmin.net All Rights Reserved {$site.name} {:__('Copyrights')} <a href="http://www.miibeian.gov.cn" target="_blank">{$site.beian}</a></p>
<p class="copyright">Copyright&nbsp;©&nbsp;2017-2018 fastadmin.net All Rights Reserved {$site.name} {:__('Copyrights')} <a href="http://www.miibeian.gov.cn" target="_blank">{$site.beian}</a></p>
</footer>
{include file="common/script" /}
... ...
... ... @@ -6,19 +6,19 @@
<div class="form-group">
<label class="control-label" for="account">{:__('Account')}</label>
<div class="controls">
<input class="form-control input-lg" id="account" type="text" name="account" value="" required="" placeholder="{:__('Email/Mobile/Username')}" autocomplete="off">
<input class="form-control input-lg" 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">
<label class="control-label" for="password">{:__('Password')}</label>
<div class="controls">
<input class="form-control input-lg" id="password" type="password" name="password" required="" placeholder="{:__('Password')}" autocomplete="off">
<input class="form-control input-lg" id="password" type="password" name="password" data-rule="required;password" placeholder="{:__('Password')}" autocomplete="off">
</div>
</div>
<div class="form-group">
<div class="controls">
<input type="checkbox" name="keeptime" checked="checked" value="86400"> {:__('Keep login')}
<input type="checkbox" name="keeplogin" checked="checked" value="1"> {:__('Keep login')}
<div class="pull-right"><a href="javascript:;" class="btn-forgot">{:__('Forgot password')}</a></div>
</div>
</div>
... ... @@ -30,43 +30,57 @@
</div>
</div>
<script type="text/html" id="resetpwdtpl">
<div style="padding:30px;">
<div class="row">
<form id="resetpwd-form" class="form-horizontal" method="POST" action="{:url('api/user/resetpwd')}">
<input type="hidden" name="action" value="changemobile" />
<div class="form-group">
<label for="c-mobile" class="control-label col-xs-12 col-sm-3">{:__('Mobile')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="mobile" name="mobile" value="" data-rule="required;mobile;remote({:url('api/validate/check_mobile_exist')}, event=resetpwd, id={$user.id})" placeholder="">
<span class="msg-box"></span>
</div>
<form id="resetpwd-form" class="form-horizontal form-layer" method="POST" action="{:url('api/user/resetpwd')}">
<div class="form-body">
<input type="hidden" name="action" value="resetpwd" />
<div class="form-group">
<label for="" class="control-label col-xs-12 col-sm-3">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="radio">
<label for="type-email"><input id="type-email" checked="checked" name="type" data-send-url="{:url('api/ems/send')}" data-check-url="{:url('api/validate/check_ems_correct')}" type="radio" value="email"> 通过邮箱找回</label>
<label for="type-mobile"><input id="type-mobile" name="type" type="radio" data-send-url="{:url('api/sms/send')}" data-check-url="{:url('api/validate/check_sms_correct')}" value="mobile"> 通过手机找回</label>
</div>
</div>
<div class="form-group">
<label for="c-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=resetpwd, mobile:#mobile)" />
<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-event="resetpwd">{:__('Send verification code')}</a>
</span>
</div>
<span class="msg-box"></span>
</div>
</div>
<div class="form-group" data-type="email">
<label for="email" class="control-label col-xs-12 col-sm-3">{:__('Email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="email" name="email" value="" data-rule="required(#type-email:checked);email;remote({:url('api/validate/check_email_exist')}, event=resetpwd, id={$user.id})" placeholder="">
<span class="msg-box"></span>
</div>
<div class="form-group">
<label for="c-newpassword" class="control-label col-xs-12 col-sm-3">{:__('New password')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="password" class="form-control" id="newpassword" name="newpassword" value="" data-rule="required;password" placeholder="">
<span class="msg-box"></span>
</div>
</div>
<div class="form-group hide" data-type="mobile">
<label for="mobile" class="control-label col-xs-12 col-sm-3">{:__('Mobile')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="mobile" name="mobile" value="" data-rule="required(#type-mobile:checked);mobile;remote({:url('api/validate/check_mobile_exist')}, event=resetpwd, id={$user.id})" placeholder="">
<span class="msg-box"></span>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-md btn-info">{:__('Ok')}</button>
</div>
<div class="form-group">
<label for="captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_ems_correct')}, event=resetpwd, email:#email)" />
<span class="input-group-btn" style="padding:0;border:none;">
<a href="javascript:;" class="btn btn-info btn-captcha" data-url="{:url('api/ems/send')}" data-type="email" data-event="resetpwd">{:__('Send verification code')}</a>
</span>
</div>
<span class="msg-box"></span>
</div>
</form>
</div>
<div class="form-group">
<label for="newpassword" class="control-label col-xs-12 col-sm-3">{:__('New password')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="password" class="form-control" id="newpassword" name="newpassword" value="" data-rule="required;password" placeholder="">
<span class="msg-box"></span>
</div>
</div>
</div>
</div>
<div class="form-group form-footer">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-md btn-info">{:__('Ok')}</button>
</div>
</div>
</form>
</script>
\ No newline at end of file
... ...
... ... @@ -107,58 +107,96 @@
</div>
<script type="text/html" id="emailtpl">
<div style="padding:30px;">
<div class="row">
<form id="email-form" class="form-horizontal" method="POST" action="{:url('api/user/changeemail')}">
<input type="hidden" name="action" value="changeemail" />
<div class="form-group">
<label for="c-email" class="control-label col-xs-12 col-sm-3">{:__('New email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="email" name="email" value="" data-rule="required;email;remote({:url('api/validate/check_email_available')}, type=changeemail, id={$user.id})" placeholder="{:__('New email')}">
<span class="msg-box"></span>
</div>
<form id="email-form" class="form-horizontal form-layer" method="POST" action="{:url('api/user/changeemail')}">
<div class="form-body">
<input type="hidden" name="action" value="changeemail" />
<div class="form-group">
<label for="c-email" class="control-label col-xs-12 col-sm-3">{:__('New Email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="email" name="email" value="" data-rule="required;email;remote({:url('api/validate/check_email_available')}, event=changeemail, id={$user.id})" placeholder="{:__('New email')}">
<span class="msg-box"></span>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-md btn-info">{:__('Submit')}</button>
</div>
<div class="form-group">
<label for="c-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" id="email-captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_ems_correct')}, event=changeemail, email:#email)" />
<span class="input-group-btn" style="padding:0;border:none;">
<a href="javascript:;" class="btn btn-info btn-captcha" data-url="{:url('api/ems/send')}" data-type="email" data-event="changeemail">获取验证码</a>
</span>
</div>
<span class="msg-box"></span>
</div>
</form>
</div>
</div>
</div>
<div class="form-footer">
<div class="form-group" style="margin-bottom:0;">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-md btn-info">{:__('Submit')}</button>
</div>
</div>
</div>
</form>
</script>
<script type="text/html" id="mobiletpl">
<div style="padding:30px;">
<div class="row">
<form id="mobile-form" class="form-horizontal" method="POST" action="{:url('api/user/changemobile')}">
<input type="hidden" name="action" value="changemobile" />
<div class="form-group">
<label for="c-mobile" class="control-label col-xs-12 col-sm-3">{:__('New mobile')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="mobile" name="mobile" value="" data-rule="required;mobile;remote({:url('api/validate/check_mobile_available')}, event=changemobile, id={$user.id})" placeholder="{:__('New mobile')}">
<span class="msg-box"></span>
</div>
<form id="mobile-form" class="form-horizontal form-layer" method="POST" action="{:url('api/user/changemobile')}">
<div class="form-body">
<input type="hidden" name="action" value="changemobile" />
<div class="form-group">
<label for="c-mobile" class="control-label col-xs-12 col-sm-3">{:__('New mobile')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="mobile" name="mobile" value="" data-rule="required;mobile;remote({:url('api/validate/check_mobile_available')}, event=changemobile, id={$user.id})" placeholder="{:__('New mobile')}">
<span class="msg-box"></span>
</div>
<div class="form-group">
<label for="c-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=changemobile, mobile:#mobile)" />
<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-event="changemobile">获取验证码</a>
</span>
</div>
<span class="msg-box"></span>
</div>
<div class="form-group">
<label for="c-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" id="mobile-captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=changemobile, mobile:#mobile)" />
<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-type="mobile" data-event="changemobile">获取验证码</a>
</span>
</div>
<span class="msg-box"></span>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-md btn-info">{:__('Submit')}</button>
</div>
</div>
</div>
<div class="form-footer">
<div class="form-group" style="margin-bottom:0;">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-md btn-info">{:__('Submit')}</button>
</div>
</form>
</div>
</div>
</div>
</script>
\ No newline at end of file
</form>
</script>
<style>
.form-layer {height:100%;min-height:150px;min-width:300px;}
.form-body {
width:100%;
overflow:auto;
top:0;
position:absolute;
z-index:10;
bottom:50px;
padding:15px;
}
.form-layer .form-footer {
height:50px;
line-height:50px;
background-color: #ecf0f1;
width:100%;
position:absolute;
z-index:200;
bottom:0;
margin:0;
}
.form-footer .form-group{
margin-left:0;
margin-right:0;
}
</style>
\ No newline at end of file
... ...
... ... @@ -7,28 +7,28 @@
<div class="form-group">
<label class="control-label required">{:__('Email')}<span class="text-success"></span></label>
<div class="controls">
<input type="text" name="email" id="email" required class="form-control input-lg" placeholder="{:__('Email')}">
<input type="text" name="email" id="email" data-rule="required;email" class="form-control input-lg" placeholder="{:__('Email')}">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">{:__('Username')}</label>
<div class="controls">
<input type="text" id="username" name="username" required class="form-control input-lg" placeholder="{:__('Username must be 6 to 30 characters')}">
<input type="text" id="username" name="username" data-rule="required;username" class="form-control input-lg" placeholder="{:__('Username must be 3 to 30 characters')}">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">{:__('Password')}</label>
<div class="controls">
<input type="password" id="password" name="password" required class="form-control input-lg" placeholder="{:__('Password must be 6 to 30 characters')}">
<input type="password" id="password" name="password" data-rule="required;password" class="form-control input-lg" placeholder="{:__('Password must be 6 to 30 characters')}">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">{:__('Mobile')}</label>
<div class="controls">
<input type="text" id="mobile" name="mobile" required class="form-control input-lg" placeholder="{:__('Mobile')}">
<input type="text" id="mobile" name="mobile" data-rule="required;mobile" class="form-control input-lg" placeholder="{:__('Mobile')}">
<p class="help-block"></p>
</div>
</div>
... ... @@ -36,7 +36,7 @@
<label class="control-label">{:__('Captcha')}</label>
<div class="controls">
<div class="input-group input-group-lg">
<input type="text" name="captcha" class="form-control" placeholder="{:__('Captcha')}" required style="border-radius: 0;" />
<input type="text" name="captcha" class="form-control" placeholder="{:__('Captcha')}" data-rule="required;length(4);integer[+]" style="border-radius: 0;" />
<span class="input-group-addon" style="padding:0;border:none;">
<img src="{:captcha_src()}" width="140" height="42" onclick="this.src = '{:captcha_src()}?r=' + Math.random();"/>
</span>
... ...
此 diff 太大无法显示。
... ... @@ -417,6 +417,35 @@ form.form-horizontal .control-label {
text-decoration: none;
font-weight: bold;
}
/* 弹窗中的表单 */
.form-layer {
height: 100%;
min-height: 150px;
min-width: 300px;
}
.form-layer .form-body {
width: 100%;
overflow: auto;
top: 0;
position: absolute;
z-index: 10;
bottom: 50px;
padding: 15px;
}
.form-layer .form-footer {
height: 50px;
line-height: 50px;
background-color: #ecf0f1;
width: 100%;
position: absolute;
z-index: 200;
bottom: 0;
margin: 0;
}
.form-layer .form-footer .form-group {
margin-left: 0;
margin-right: 0;
}
#treeview .jstree-container-ul .jstree-node {
display: block;
clear: both;
... ...
... ... @@ -14,15 +14,6 @@ body {
padding-top: 50px;
font-size: 13px;
}
.wow {
visibility: hidden;
}
.img-portfolio {
margin-bottom: 30px;
}
.img-hover:hover {
opacity: 0.8;
}
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0;
... ... @@ -66,12 +57,22 @@ body {
height: 80px;
object-fit: cover;
}
.layui-layer-content {
clear: both;
}
.layui-layer-fast-msg {
min-width: 100px;
border-radius: 2px;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
}
.layui-layer-fast-msg .layui-layer-content {
padding: 12px 25px;
text-align: center;
}
#header-navbar li.dropdown ul.dropdown-menu {
min-width: 94px;
}
.panel-col {
min-height: 400px;
}
.panel-default {
padding: 0 15px;
border-color: #e4ecf3;
... ... @@ -132,23 +133,6 @@ body {
background: none;
}
}
@media (max-width: 1920px) {
.panel-default > .panel-body .zuixin {
width: 100%;
border-bottom: 1px solid #f5f5f5;
padding-bottom: 5px;
margin-bottom: 10px;
}
}
@media (max-width: 992px) {
.panel-default > .panel-body .zuixin {
width: 50%;
padding-bottom: 5px;
margin-bottom: 10px;
float: left;
padding-right: 5px;
}
}
.panel-primary > .panel-heading {
background-color: #46c37b;
color: #fff;
... ... @@ -226,84 +210,35 @@ body {
line-height: 1.5;
padding: 4px 13px;
}
.metas {
position: relative;
padding: 10px;
color: #c1c1c1;
/* 弹窗中的表单 */
.form-layer {
height: 100%;
min-height: 150px;
min-width: 300px;
}
.metas i {
margin-right: 5px;
.form-layer .form-body {
width: 100%;
overflow: auto;
top: 0;
position: absolute;
z-index: 10;
bottom: 50px;
padding: 15px;
}
.metas .addon-price {
float: right;
.form-layer .form-footer {
height: 50px;
line-height: 50px;
background-color: #ecf0f1;
width: 100%;
position: absolute;
z-index: 200;
bottom: 0;
margin: 0;
}
.metas .price {
color: #e83d2c;
font-size: 14px;
.form-layer .form-footer .form-group {
margin-left: 0;
margin-right: 0;
}
.metas .free {
color: #238312;
}
.metas .comment {
margin-left: 10px;
}
@media (max-width: 767px) {
.metas .metas {
padding: 5px;
}
.metas .comment {
display: none;
}
}
.text-line {
position: relative;
padding: 30px 0;
text-align: center;
}
.text-line.small {
padding: 10px 0;
}
.text-line.small h5 {
font-size: 14px;
}
.text-line.small h5 > span {
padding: 0 20px;
}
.text-line h5 {
position: relative;
margin-bottom: 20px;
font-size: 32px;
z-index: 1;
color: #313131;
}
.text-line h5 > i {
padding-left: 20px;
background: #fff;
}
.text-line h5 > span {
padding: 0 40px;
}
.text-line .subtitle {
font-size: 16px;
color: #919191;
}
@media (max-width: 767px) {
.text-line {
padding: 20px 0;
}
.text-line h5 {
font-size: 16px;
}
.text-line .subtitle {
font-size: 14px;
}
}
@media (max-width: 767px) {
header.carousel .carousel {
height: 70%;
}
}
footer.footer {
width: 100%;
color: #aaa;
... ... @@ -311,87 +246,61 @@ footer.footer {
margin-top: 25px;
position: fixed;
bottom: 0;
z-index: 99;
}
footer.footer ul {
margin: 60px 0 30px 0;
padding: 0;
}
footer.footer ul li.f-tit {
margin-bottom: 10px;
font-size: 14px;
color: #fff;
}
footer.footer ul li {
line-height: 26px;
white-space: nowrap;
list-style: none;
margin: 0;
padding: 0;
}
footer.footer ul li a {
color: #aaa;
}
footer.footer ul li a:hover {
color: #aaa;
text-decoration: underline !important;
}
footer.footer .address {
footer.footer .copyright {
line-height: 50px;
text-align: center;
background: #393939;
margin: 0;
}
footer p.address a {
footer.footer .copyright a {
color: #aaa;
}
footer p.address a:hover {
footer.footer .copyright a:hover {
color: #fff;
}
@media (max-width: 767px) {
footer.footer {
overflow: hidden;
}
footer.footer .container {
margin: 0 20px;
}
footer.footer ul {
margin: 20px 0 10px 0;
}
footer.footer .address {
padding: 0 10px;
}
}
.rotate {
-webkit-transition-duration: 0.8s;
-moz-transition-duration: 0.8s;
-o-transition-duration: 0.8s;
transition-duration: 0.8s;
-webkit-transition-property: transform;
transition-property: transform;
-webkit-transition-property: -webkit-transform;
-moz-transition-property: -moz-transform;
-o-transition-property: -o-transform;
transition-property: transform;
transition-property: -webkit-transform,-moz-transform,-o-transform,transform;
overflow: hidden;
}
.rotate:hover {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-o-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
.user-section {
background: #fff;
padding: 15px;
margin-bottom: 20px;
-webkit-border-radius: 4px;
-webkit-background-clip: padding-box;
-moz-border-radius: 4px;
-moz-background-clip: padding;
border-radius: 4px;
background-clip: padding-box;
border: 1px solid #e4ecf3;
}
.login-section {
margin: 50px auto;
width: 460px;
-webkit-border-radius: 0;
-webkit-background-clip: padding-box;
-moz-border-radius: 0;
-moz-background-clip: padding;
border-radius: 0;
background-clip: padding-box;
}
.login-section.login-section-weixin {
min-height: 315px;
... ... @@ -433,42 +342,43 @@ footer p.address a:hover {
.login-section .control-label {
font-size: 13px;
}
@media (max-width: 767px) {
.login-section {
width: 100%;
margin: 20px auto;
}
.login-section .login-main {
padding: 20px 0 0 0;
}
.login-section .n-bootstrap .form-group {
position: relative;
}
@media (min-width: 768px) {
.login-modal {
width: 350px;
}
.login-modal .modal-body {
padding: 30px 30px 15px 30px;
}
.login-modal .modal-footer {
padding: 30px;
}
.login-section .n-bootstrap .input-group {
position: inherit;
}
#content-container {
margin: 30px auto;
min-height: 400px;
.login-section .n-bootstrap .n-right {
margin-top: 0;
top: 0;
position: absolute;
left: 0;
text-align: right;
width: 100%;
}
@media (max-width: 767px) {
#content-container {
min-height: 250px;
}
.login-section .n-bootstrap .n-right .msg-wrap {
position: relative;
}
main.content {
width: 100%;
overflow: auto;
top: 0;
position: absolute;
z-index: 10;
bottom: 50px;
padding: 15px;
padding-top: 67px;
}
.sidenav {
padding: 20px 0 10px 0;
margin-bottom: 20px;
background-color: #fff;
-webkit-border-radius: 4px;
-webkit-background-clip: padding-box;
-moz-border-radius: 4px;
-moz-background-clip: padding;
border-radius: 4px;
background-clip: padding-box;
border: 1px solid #e4ecf3;
}
.sidenav .list-group:last-child {
... ... @@ -483,8 +393,11 @@ footer p.address a:hover {
}
.sidenav .list-group .list-group-item {
-webkit-border-radius: 0;
-webkit-background-clip: padding-box;
-moz-border-radius: 0;
-moz-background-clip: padding;
border-radius: 0;
background-clip: padding-box;
border: none;
padding: 0;
border-left: 2px solid transparent;
... ... @@ -492,8 +405,11 @@ footer p.address a:hover {
.sidenav .list-group .list-group-item:last-child,
.sidenav .list-group .list-group-item:first-child {
-webkit-border-radius: 0;
-webkit-background-clip: padding-box;
-moz-border-radius: 0;
-moz-background-clip: padding;
border-radius: 0;
background-clip: padding-box;
}
.sidenav .list-group .list-group-item:hover {
background-color: #f5f5f5;
... ... @@ -510,9 +426,6 @@ footer p.address a:hover {
.sidenav .list-group .list-group-item.active > a {
color: #46c37b;
}
.flarum-section ul li a {
font-size: 13px;
}
.nav li .avatar-text,
.nav li .avatar-img {
height: 30px;
... ... @@ -551,4 +464,24 @@ footer p.address a:hover {
width: 48px;
height: 48px;
}
@media (max-width: 767px) {
main.content {
position: inherit;
padding: 15px 0;
}
.login-section {
width: 100%;
margin: 20px auto;
}
.login-section .login-main {
padding: 20px 0 0 0;
}
footer.footer {
position: inherit;
}
footer.footer .copyright {
padding: 10px;
line-height: 30px;
}
}
/*# sourceMappingURL=frontend.css.map */
\ No newline at end of file
... ...
... ... @@ -185,7 +185,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
data: {action: 'refreshmenu'}
}, function (data) {
$(".sidebar-menu li:not([data-rel='external'])").remove();
$(data.menulist).insertBefore($(".sidebar-menu li:first"));
$(".sidebar-menu").prepend(data.menulist);
$("#nav ul li[role='presentation'].active a").trigger('click');
return false;
}, function () {
... ...
... ... @@ -42,7 +42,7 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
return;
}
}
Toastr.error(ret.msg + "(code:" + ret.code + ")");
Toastr.error(ret.msg);
},
//服务器响应数据后
onAjaxResponse: function (response) {
... ...
... ... @@ -2,36 +2,48 @@ define(['fast'], function (Fast) {
var Frontend = {
api: Fast.api,
init: function () {
//发送手机验证码
var si = {};
//发送验证码
$(document).on("click", ".btn-captcha", function (e) {
var mobile = $(this).closest("form").find("#mobile");
if (mobile.val() == "") {
Layer.alert("手机号码不能为空!");
var type = $(this).data("type") ? $(this).data("type") : 'mobile';
var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form"));
var text = type === 'email' ? '邮箱' : '手机号码';
if (element.val() === "") {
Layer.msg(text + "不能为空!");
element.focus();
return false;
} else if (!mobile.val().match(/^1[3-9]\d{9}$/)) {
Layer.alert("请输入正确的手机号码!");
} else if (type === 'mobile' && !element.val().match(/^1[3-9]\d{9}$/)) {
Layer.msg("请输入正确的" + text + "!");
element.focus();
return false;
} else if (type === 'email' && !element.val().match(/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/)) {
Layer.msg("请输入正确的" + text + "!");
element.focus();
return false;
}
var that = this;
mobile.isValid(function (v) {
element.isValid(function (v) {
if (v) {
$(that).addClass("disabled", true).text("获取中...");
var si;
Frontend.api.ajax({url: $(that).data("url"), data: {event: $(that).data("event"), mobile: mobile.val()}}, function () {
clearInterval(si);
$(that).addClass("disabled", true).text("发送中...");
var data = {event: $(that).data("event")};
data[type] = element.val();
Frontend.api.ajax({url: $(that).data("url"), data: data}, function () {
clearInterval(si[type]);
var seconds = 60;
si = setInterval(function () {
si[type] = setInterval(function () {
seconds--;
if (seconds <= 0) {
clearInterval(si);
$(that).removeClass("disabled", false).text("获取验证码");
$(that).removeClass("disabled").text("发送验证码");
} else {
$(that).addClass("disabled", true).text(seconds + "秒后可再次发送");
$(that).addClass("disabled").text(seconds + "秒后可再次发送");
}
}, 1000);
}, function () {
$(that).removeClass("disabled").text('发送验证码');
});
} else {
Layer.alert("请确认已经输入了正解的手机号!");
Layer.msg("请确认已经输入了正确的" + text + "!");
}
});
... ...
define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, undefined, Frontend, Form, Template) {
var validatoroptions = {
invalid: function (form, errors) {
$.each(errors, function (i, j) {
Layer.msg(j);
});
}
};
var Controller = {
login: function () {
//本地验证未通过时提示
$("#login-form").data("validator-options", {
invalid: function (form, errors) {
$.each(errors, function (i, j) {
Layer.alert(j);
});
},
$("#login-form").data("validator-options", validatoroptions);
$(document).on("change", "input[name=type]", function () {
var type = $(this).val();
$("div.form-group[data-type]").addClass("hide");
$("div.form-group[data-type='" + type + "']").removeClass("hide");
$('#resetpwd-form').validator("setField", {
captcha: "required;length(4);integer[+];remote(" + $(this).data("check-url") + ", event=resetpwd, " + type + ":#" + type + ")",
});
$(".btn-captcha").data("url", $(this).data("send-url")).data("type", type);
});
//为表单绑定事件
... ... @@ -27,7 +38,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
Layer.open({
type: 1,
title: "修改",
area: ["450px", "auto"],
area: ["450px", "355px"],
content: content,
success: function (layero) {
Form.api.bindevent($("#resetpwd-form", layero), function (data) {
... ... @@ -39,13 +50,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
},
register: function () {
//本地验证未通过时提示
$("#register-form").data("validator-options", {
invalid: function (form, errors) {
$.each(errors, function (i, j) {
Layer.alert(j);
});
},
});
$("#register-form").data("validator-options", validatoroptions);
//为表单绑定事件
Form.api.bindevent($("#register-form"), function (data, ret) {
... ... @@ -56,13 +61,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
},
changepwd: function () {
//本地验证未通过时提示
$("#resetpwd-form").data("validator-options", {
invalid: function (form, errors) {
$.each(errors, function (i, j) {
Layer.alert(j);
});
},
});
$("#changepwd-form").data("validator-options", validatoroptions);
//为表单绑定事件
Form.api.bindevent($("#changepwd-form"), function (data, ret) {
... ... @@ -78,26 +77,23 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
$(".profile-user-img").prop("src", url);
Toastr.success("上传成功!");
});
//为表单绑定事件
Form.api.bindevent($("#profile-form"), function (data) {
});
Form.api.bindevent($("#email-form"), function (data) {
Layer.closeAll();
$("#basic-form #email").val($("#email").val());
});
Form.api.bindevent($("#mobile-form"), function (data) {
Layer.closeAll();
$("#basic-form #mobile").val($("#mobile").val());
});
Form.api.bindevent($("#profile-form"));
$(document).on("click", ".btn-change", function () {
var that = this;
var id = $(this).data("type") + "tpl";
var content = Template(id, {});
Layer.open({
type: 1,
title: "修改",
area: ["450px", "auto"],
area: ["400px", "250px"],
content: content,
success: function (layero) {
var form = $("form", layero);
Form.api.bindevent(form, function (data) {
Layer.closeAll();
console.log(123);
});
}
});
});
}
... ...
... ... @@ -696,7 +696,7 @@ define('fast',['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, u
return;
}
}
Toastr.error(ret.msg + "(code:" + ret.code + ")");
Toastr.error(ret.msg);
},
//服务器响应数据后
onAjaxResponse: function (response) {
... ...
... ... @@ -696,7 +696,7 @@ define('fast',['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, u
return;
}
}
Toastr.error(ret.msg + "(code:" + ret.code + ")");
Toastr.error(ret.msg);
},
//服务器响应数据后
onAjaxResponse: function (response) {
... ... @@ -977,36 +977,48 @@ define('frontend',['fast'], function (Fast) {
var Frontend = {
api: Fast.api,
init: function () {
//发送手机验证码
var si = {};
//发送验证码
$(document).on("click", ".btn-captcha", function (e) {
var mobile = $(this).closest("form").find("#mobile");
if (mobile.val() == "") {
Layer.alert("手机号码不能为空!");
var type = $(this).data("type") ? $(this).data("type") : 'mobile';
var element = $(this).data("input-id") ? $("#" + $(this).data("input-id")) : $("input[name='" + type + "']", $(this).closest("form"));
var text = type === 'email' ? '邮箱' : '手机号码';
if (element.val() === "") {
Layer.msg(text + "不能为空!");
element.focus();
return false;
} else if (!mobile.val().match(/^1[3-9]\d{9}$/)) {
Layer.alert("请输入正确的手机号码!");
} else if (type === 'mobile' && !element.val().match(/^1[3-9]\d{9}$/)) {
Layer.msg("请输入正确的" + text + "!");
element.focus();
return false;
} else if (type === 'email' && !element.val().match(/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/)) {
Layer.msg("请输入正确的" + text + "!");
element.focus();
return false;
}
var that = this;
mobile.isValid(function (v) {
element.isValid(function (v) {
if (v) {
$(that).addClass("disabled", true).text("获取中...");
var si;
Frontend.api.ajax({url: $(that).data("url"), data: {event: $(that).data("event"), mobile: mobile.val()}}, function () {
clearInterval(si);
$(that).addClass("disabled", true).text("发送中...");
var data = {event: $(that).data("event")};
data[type] = element.val();
Frontend.api.ajax({url: $(that).data("url"), data: data}, function () {
clearInterval(si[type]);
var seconds = 60;
si = setInterval(function () {
si[type] = setInterval(function () {
seconds--;
if (seconds <= 0) {
clearInterval(si);
$(that).removeClass("disabled", false).text("获取验证码");
$(that).removeClass("disabled").text("发送验证码");
} else {
$(that).addClass("disabled", true).text(seconds + "秒后可再次发送");
$(that).addClass("disabled").text(seconds + "秒后可再次发送");
}
}, 1000);
}, function () {
$(that).removeClass("disabled").text('发送验证码');
});
} else {
Layer.alert("请确认已经输入了正解的手机号!");
Layer.msg("请确认已经输入了正确的" + text + "!");
}
});
... ...
... ... @@ -452,6 +452,34 @@ form.form-horizontal .control-label {
ins{width:110px;display: inline-block;text-decoration:none;font-weight: bold;}
}
/* 弹窗中的表单 */
.form-layer {
height:100%;min-height:150px;min-width:300px;
.form-body {
width:100%;
overflow:auto;
top:0;
position:absolute;
z-index:10;
bottom:50px;
padding:15px;
}
.form-footer {
height:50px;
line-height:50px;
background-color: #ecf0f1;
width:100%;
position:absolute;
z-index:200;
bottom:0;
margin:0;
}
.form-footer .form-group{
margin-left:0;
margin-right:0;
}
}
#treeview {
.jstree-container-ul .jstree-node{
display:block;clear:both;
... ...
... ... @@ -32,16 +32,6 @@ body {
font-size:13px;
}
.wow { visibility: hidden; }
.img-portfolio {
margin-bottom: 30px;
}
.img-hover:hover {
opacity: 0.8;
}
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0;
... ... @@ -66,7 +56,6 @@ body {
display:inherit;
}
/*预览区域*/
.plupload-preview {
padding:10px;
... ... @@ -89,13 +78,24 @@ body {
}
}
.layui-layer-content {
clear: both;
}
.layui-layer-fast-msg {
min-width: 100px;
border-radius: 2px;
background-color: rgba(0,0,0,.6);
color: #fff;
.layui-layer-content {
padding: 12px 25px;
text-align: center;
}
}
#header-navbar li.dropdown ul.dropdown-menu {
min-width:94px;
}
.panel-col {
min-height: 400px;
}
.panel-default {
padding: 0 15px;
border-color: #e4ecf3;
... ... @@ -154,17 +154,6 @@ body {
}
}
@media (max-width: 1920px) {
.panel-default > .panel-body .zuixin{
width:100%;border-bottom: 1px solid #f5f5f5; padding-bottom:5px; margin-bottom:10px;
}
}
@media (max-width: 992px) {
.panel-default > .panel-body .zuixin{
width:50%; padding-bottom:5px; margin-bottom:10px; float:left; padding-right:5px;
}
}
.panel-primary {
> .panel-heading {
background-color: #46c37b;
... ... @@ -203,6 +192,7 @@ body {
}
}
}
@media (max-width: 767px) {
.panel-page {
padding: 15px;
... ... @@ -235,143 +225,65 @@ body {
padding: 4px 13px;
}
.metas {
position: relative;
padding: 10px;
color: #c1c1c1;
i {
margin-right: 5px;
}
.addon-price {
float: right;
}
.price {
color: #e83d2c;
font-size: 14px;
margin-right: 0;
}
.free {
color: #238312;
}
.comment {
margin-left: 10px;
}
}
@media (max-width: 767px) {
.metas {
.metas {
padding: 5px;
}
.comment {
display: none;
}
}
}
.text-line {
position: relative;
padding: 30px 0;
text-align: center;
&.small {
padding: 10px 0;
h5 {
font-size: 14px;
> span {
padding: 0 20px;
}
}
}
h5 {
position: relative;
margin-bottom: 20px;
font-size: 32px;
z-index: 1;
color: #313131;
> i {
padding-left: 20px;
background: #fff;
}
> span {
padding: 0 40px;
}
}
.subtitle {
font-size: 16px;
color: #919191;
}
}
@media (max-width: 767px) {
.text-line {
padding: 20px 0;
h5 {
font-size: 16px;
}
.subtitle {
font-size: 14px;
/* 弹窗中的表单 */
.form-layer {
height:100%;min-height:150px;min-width:300px;
.form-body {
width:100%;
overflow:auto;
top:0;
position:absolute;
z-index:10;
bottom:50px;
padding:15px;
}
.form-footer {
height:50px;
line-height:50px;
background-color: #ecf0f1;
width:100%;
position:absolute;
z-index:200;
bottom:0;
margin:0;
}
.form-footer .form-group{
margin-left:0;
margin-right:0;
}
}
footer.footer{
width:100%;color: #aaa;background: #555;margin-top:25px;position: fixed;bottom: 0;z-index:99;
.copyright{
line-height: 50px;text-align: center;background: #393939;margin:0;
a{
color: #aaa;
&:hover{color: #fff;}
}
}
}
@media(max-width:767px) {
header.carousel .carousel {
height: 70%;
}
}
footer.footer{width:100%;color: #aaa;background: #555;margin-top:25px;position: fixed;bottom: 0;}
footer.footer ul{margin:60px 0 30px 0;padding:0;}
footer.footer ul li.f-tit{margin-bottom:10px;font-size: 14px;color: #fff;}
footer.footer ul li{line-height: 26px;white-space: nowrap;list-style: none;margin:0;padding:0;}
footer.footer ul li a{color: #aaa;}
footer.footer ul li a:hover{color: #aaa;text-decoration: underline !important;}
footer.footer .address{line-height: 50px;text-align: center;background: #393939;margin:0;}
footer p.address a{color: #aaa;}
footer p.address a:hover{color: #fff;}
@media(max-width:767px) {
footer.footer {overflow: hidden;}
footer.footer .container{margin:0 20px;}
footer.footer ul {margin:20px 0 10px 0;}
footer.footer .address{padding:0 10px;}
}
.rotate{
-webkit-transition-duration: 0.8s;
-moz-transition-duration: 0.8s;
-o-transition-duration: 0.8s;
transition-duration: 0.8s;
-webkit-transition-property: -webkit-transform;
-moz-transition-property: -moz-transform;
-o-transition-property: -o-transform;
transition-property: transform;
.transition-duration(0.8s);
.transition-property(transform);
overflow:hidden;
&:hover
{
-webkit-transform:rotate(360deg);
-moz-transform:rotate(360deg);
-o-transform:rotate(360deg);
&:hover{
.transform(rotate(360deg));
}
}
.user-section {
background: #fff;
padding: 15px;
margin-bottom: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
.border-radius(4px);
border: 1px solid #e4ecf3;
}
.login-section {
margin: 50px auto;
width: 460px;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
.border-radius(0);
&.login-section-weixin {
min-height: 315px;
}
... ... @@ -403,47 +315,42 @@ footer p.address a:hover{color: #fff;}
.control-label {
font-size:13px;
}
}
@media (max-width: 767px) {
.login-section {
width: 100%;
margin: 20px auto;
.login-main {
padding: 20px 0 0 0;
.n-bootstrap {
.form-group {
position:relative;
}
}
}
@media (min-width: 768px) {
.login-modal {
width: 350px;
.modal-body {
padding: 30px 30px 15px 30px;
.input-group {
position: inherit;
}
.modal-footer {
padding: 30px;
.n-right {
margin-top:0;
top:0;
position:absolute;
left:0;
text-align:right;
width:100%;
.msg-wrap {
position:relative;
}
}
}
}
#content-container {
margin: 30px auto;
min-height: 400px;
}
@media (max-width: 767px) {
#content-container {
min-height: 250px;
}
main.content {
width:100%;
overflow:auto;
top:0;
position:absolute;
z-index:10;
bottom:50px;
padding:15px;
padding-top:67px;
}
.sidenav {
padding: 20px 0 10px 0;
margin-bottom: 20px;
background-color: #fff;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
.border-radius(4px);
border: 1px solid #e4ecf3;
.list-group{
&:last-child {
... ... @@ -457,16 +364,12 @@ footer p.address a:hover{color: #fff;}
font-size:14px;
}
.list-group-item {
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
.border-radius(0);
border: none;
padding: 0;
border-left: 2px solid transparent;
&:last-child,&:first-child {
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
.border-radius(0);
}
&:hover {
background-color: #f5f5f5;
... ... @@ -487,9 +390,6 @@ footer p.address a:hover{color: #fff;}
}
}
.flarum-section ul li a{font-size:13px;}
.nav li{
.avatar-text,.avatar-img {
height:30px;
... ... @@ -521,8 +421,28 @@ footer p.address a:hover{color: #fff;}
}
.avatar-img {
font-size:0;
img{
border-radius:48px;
width:48px;height:48px;
}
}
@media (max-width: 767px) {
main.content {
position:inherit;
padding:15px 0;
}
.login-section {
width: 100%;
margin: 20px auto;
.login-main {
padding: 20px 0 0 0;
}
}
footer.footer {
position:inherit;
.copyright{padding:10px;line-height:30px;}
}
}
.avatar-img img{
border-radius:48px;
width:48px;height:48px;
}
\ No newline at end of file
... ...