Authored by 陈轩

save

/* eslint no-unused-vars: ["error", { "args": "none" }] */
/**
* 登录
* @author: Bi Kai<kai.bi@yoho.cn>
... ... @@ -93,6 +94,7 @@ const local = {
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
smsLoginUrl: '/passport/sms_login',
registerUrl: '/passport/reg/index', // 注册的URL链接
aliLoginUrl: '/passport/login/alipay', // 支付宝快捷登录的URL链接
weiboLoginUrl: '/passport/login/sina', // 微博登录的URL链接
... ...
/* eslint no-unused-vars: ["error", { "args": "none" }] */
'use strict';
const RegService = require('../models/reg-service');
const PhoneService = require('../models/phone-service');
// constrant
const CODE_REQUIRED = '请输入校验码';
// 短信登录 第一步: 展现页面
const step1 = (req, res, next) => {
let template = 'sms/login';
let viewData = {
module: 'passport',
page: 'sms-login',
isPassportPage: true,
headerText: '手机号码快捷登录',
areaCode: '+86', // 默认的区号
countrys: RegService.getAreaData() // 地区信息列表
};
res.render(template, viewData);
};
// 短信登录 第二步: 输入 校验码
const step2 = (req, res, next) => {
const mobile = req.session.sms_login.mobile;
const area = req.session.sms_login.area;
const template = 'sms/check';
const viewData = {
module: 'passport',
page: 'sms-check',
isPassportPage: true,
headerText: '手机号码快捷登录',
interval: 60,
mobile,
area
};
res.render(template, viewData);
};
// 短信登录 第二步: 设置密码 (针对 改手机未注册用户)
const step3 = (req, res, next) => {
};
// 短信 登录
exports.loginPage = (req, res, next) => {
let step = Number(req.query.step) || 1;
switch (step) {
case 2: step2(req, res, next); break;
case 3: step3(req, res, next); break;
case 1:
default:
step1(req, res, next);
}
};
exports.token = (req, res, next) => {
if (!req.xhr) {
return next(404);
}
let area = req.query.area;
let mobile = req.query.mobile;
PhoneService.sendSMS({ mobile, area }).then(result => {
if (result.code === 200) {
req.session.sms_login = {
area,
mobile
};
result.redirect = '/passport/sms_login?step=2';
res.json(result);
return;
}
res.json(result);
});
};
exports.check = (req, res, next) => {
if (!req.xhr) {
return next(404);
}
// TODO: 防御性
let code = req.query.code || '';
const mobile = req.session.sms_login.mobile;
const area = req.session.sms_login.mobile;
code = code.trim();
if (!code) {
res.json({
code: 404,
message: CODE_REQUIRED
});
}
Promise.all([
PhoneService.checkUserPhoneExist(mobile, area),
PhoneService.verifySMS(mobile, area, code)
])
.then(result => {
let r1 = result[0] || {};
let r2 = result[1] || {};
let redirect;
// 验证码 校验失败
if (r2.code !== 200) {
res.json(r2);
return;
}
// 检测 手机号 是否注册 异常
if (r1.code !== 200) {
res.json(r1);
return;
}
if (r1.data.is_register === 'Y') {
redirect = '';
} else {
redirect = '/passport/sms_login?step=3';
}
res.json({
code: 200,
redirect
});
})
.catch();
};
exports.password = (req, res, next) => {
res.render('sms/password', {
module: 'passport',
page: 'sms-login',
isPassportPage: true,
headerText: '设置密码',
});
};
... ...
/* eslint no-unused-vars: ["error", { "args": "none" }] */
'use strict';
const API = global.yoho.API;
class PhoneService {
// 校验 手机 是否 已注册
// http://git.yoho.cn/yoho-documents/api-interfaces/blob/master/个人中心/验证码登录/校验是否是注册用户.md
static checkUserPhoneExist(mobile, area) {
return API.get('', {
method: 'app.passport.checkUserExist',
mobile,
area
});
}
// 手机号 自动登录
// http://git.yoho.cn/yoho-documents/api-interfaces/blob/master/个人中心/验证码登录/手机号自动登录.md
static autoSignin(profile, area) {
return API.get('', {
method: 'app.passport.autoSignin',
profile,
area
});
}
// 发送 验证码
// http://git.yoho.cn/yoho-documents/api-interfaces/blob/master/个人中心/验证码登录/发送验证码.md
static sendSMS(mobile, area, type) {
if (process.env.NODE_ENV === 'development') {
return new Promise((resolve, reject) => {
return resolve({
alg: 'SALT_MD5',
code: 200,
data: {},
md5: '6d729d4b35f10fc73531210bd7ecff91',
message: '发送成功.'
});
});
}
return API.get('', {
method: 'app.message.sendSms',
mobile,
area,
type
});
}
// 校验 验证码
// http://git.yoho.cn/yoho-documents/api-interfaces/blob/master/个人中心/验证码登录/验证验证码.md
static verifySMS(mobile, area, code) {
if (process.env.NODE_ENV === 'development') {
return new Promise((resolve, reject) => {
return resolve({
alg: 'SALT_MD5',
code: 200,
data: {
is_pass: 'Y'
},
md5: '6d729d4b35f10fc73531210bd7ecff91',
message: '发送成功.'
});
});
}
return API.get('', {
method: 'app.user.verifySmsCode',
mobile,
area,
code
});
}
}
module.exports = PhoneService;
... ...
... ... @@ -12,6 +12,7 @@ const login = require(cRoot + '/login');
const back = require(cRoot + '/back');
const bind = require(cRoot + '/bind');
const reg = require(cRoot + '/reg');
const smsLogin = require(cRoot +'/sms');
const router = express.Router(); // eslint-disable-line
... ... @@ -33,6 +34,12 @@ router.get('/passport/international', login.common.beforeLogin, login.local.inte
// 本地登录
router.post('/passport/login/auth', login.local.login);
// SMS 短信
router.get('/passport/sms_login', smsLogin.loginPage);
router.get('/passport/sms_login/token', smsLogin.token); // only ajax;
router.get('/passport/sms_login/check', smsLogin.check);
router.get('/passport/sms_login/password', smsLogin.password);
// 微信登录
router.get('/passport/login/wechat', login.common.beforeLogin, login.wechat.login);
router.get('/passport/login/wechat/callback', login.wechat.callback);
... ...
... ... @@ -10,7 +10,7 @@
</div>
<span id="btn-login" class="btn btn-login disable">登录</span>
<p class="op-container">
<a class="go-register" href={{registerUrl}}>免费注册</a>
<a class="sms-login" href={{smsLoginUrl}}>手机号码快捷登录</a>
<span id="forget-pwd" class="forget-pwd">忘记密码</span>
</p>
<div class="third-party-login">
... ... @@ -22,6 +22,10 @@
</div>
</div>
<a class="international" href={{internationalUrl}}>International Customer</a>
<div class="go-register">
<i class="iconfont">&#xe610;</i>
<a href={{registerUrl}}>注册Yoho!Family</a>
</div>
<div class="login-tip">
<div class="info-icon"></div>
Yoho!Family账号可登录Yoho!Buy有货
... ...
<div class="sms-login passport-page yoho-page">
{{> passport/header}}
<div class="content">
<p class="sms-login__msg">验证码已发至&nbsp;<span class="tel">+86 133601454888</span></p>
<div class="input-container input-group sms-input row">
<input id="sms-code" class="input" type="text" placeholder="验证码">
<span class="input-addon">
<button type="button" id="resend-sms">重发验证码</button>
</span>
</div>
<button id="btn-next" class="btn btn-next disable row" type="button">登录</button>
</div>
<input type="hidden" name="mobile" value="{{mobile}}">
<input type="hidden" name="area" value="{{area}}">
</div>
<script>
var interval = {{interval}};
</script>
\ No newline at end of file
... ...
<div class="sms-login-page passport-page yoho-page">
{{> passport/header}}
<div class="content">
{{> passport/country_list}}
<div class="input-container phone-container row has-clear">
<span id="area-code" class="area-code">{{areaCode}}</span>
<input id="phone-num" class="input phone-num" type="text" placeholder="手机号">
</div>
<span id="btn-next" class="btn btn-next disable row">获取短信验证码</span>
</div>
</div>
\ No newline at end of file
... ...
<div class="sms-login passport-page yoho-page">
{{> passport/header}}
<div class="content">
<p class="sms-login__msg small">你以后还可以使用手机号码 + 密码的形式登录有货哦!</p>
<div class="input-container row has-eye">
<input id="pwd" class="pwd input" type="password" placeholder="密码">
<div class="eye close" id="eye"></div>
</div>
<span id="btn-next" class="btn btn-next disable row">确定</span>
</div>
</div>
\ No newline at end of file
... ...
var tip = require('plugin/tip');
var $resendBtn,
$nextBtn,
$smsCode,
mobile, area;
var page = {
time: 3,
resendText: '重发验证码',
timerId: null,
init: function() {
this.domInit();
this.bindEvents();
this.countDown();
},
domInit: function() {
$resendBtn = $('#resend-sms');
$nextBtn = $('#btn-next');
$smsCode = $('#sms-code');
mobile = $('#mobile').val();
area = $('#area').val();
},
bindEvents: function() {
var self = this;
$resendBtn.on('click', function() {
self.resendSMS();
});
$smsCode.on('input', function() {
var hasVal = Boolean($.trim(this.value));
$nextBtn.toggleClass('disable', !hasVal);
});
$nextBtn.on('click', function() {
self.submit();
});
},
countDown: function() {
var self = this;
var second = this.time;
if (this.timerId) {
return;
}
$resendBtn.prop('disable', true);
this.timerId = setInterval(function() {
var txt = self.resendText;
second = second - 1;
if (second < 0) {
clearInterval(self.timerId);
self.timerId = null;
$resendBtn.prop('disable', false);
} else {
txt = second + 's';
}
$resendBtn.text(txt);
}, 1000);
},
resendSMS: function() {
var self = this;
if ($resendBtn.prop('disable')) {
return;
}
$.get('/passport/sms_login/token', {
area: area,
mobile: mobile,
})
.done(function() {
self.countDown();
});
},
submit: function() {
var code = $.trim($smsCode.val());
$.get('/passport/sms_login/check', {
code: code
})
.done(function(res) {
if (res.code === 200) {
location.href = res.redirect;
return;
}
tip.show(res.message);
})
.fail(function() {
tip.show('出错了, 请重试');
});
}
};
$(function() {
page.init();
});
... ...
'use strict';
var tip = require('plugin/tip');
var api = require('./api');
var $countrySelect,
$areaCode,
$nextBtn,
$phoneNum;
var page = {
disableAjax: false,
init: function() {
this.domInit();
this.bindEvent();
},
domInit: function() {
$countrySelect = $('#country-select');
$areaCode = $('#area-code');
$nextBtn = $('#btn-next');
$phoneNum = $('#phone-num');
},
bindEvent: function() {
var self = this;
$countrySelect.on('change', function() {
$areaCode.text(this.value);
});
$phoneNum.on('input', function() {
var bool = Boolean($.trim(this.value));
$nextBtn.toggleClass('disable', !bool);
});
$nextBtn.on('click', function() {
!self.disableAjax && self.goNext();
});
},
// 提交按钮
goNext: function() {
var areaCode = $countrySelect.val();
var phone = $.trim($phoneNum.val());
var self = this;
if ($nextBtn.hasClass('disable')) {
return;
}
if (!api.phoneRegx[areaCode].test(phone)) {
tip.show('手机号码格式不正确, 请重新输入');
return;
}
$.get('/passport/sms_login/token', {
area: areaCode.replace('+', ''),
mobile: phone
})
.done(function(data) {
if (data.code === 200) {
location.href = data.redirect;
} else {
tip.show(data.message);
}
})
.fail(function() {
tip.show('出错了, 请重试');
})
.always(function() {
self.disableAjax = false;
});
}
};
$(function() {
page.init();
});
... ...
var $eyeBtn,
$pwd,
$nextBtn;
var page = {
init: function() {
this.domInit();
this.bindEvent();
},
domInit: function() {
$eyeBtn = $('#eye');
$pwd = $('#pwd');
$nextBtn = $('#btn-next');
},
bindEvent: function() { },
};
$(function() {
page.init();
});
\ No newline at end of file
... ...
... ... @@ -6,6 +6,7 @@
@import "layout/swiper";
@import "layout/header";
@import "layout/footer";
@import "layout/form";
@import "common/index";
@import "channel/index";
@import "product/index";
... ...
.input-group {
display: table;
.input {
width: 100%;
}
.input,
.input-addon {
display: table-cell;
vertical-align: middle;
}
}
\ No newline at end of file
... ...
@import "common";
@import "register";
@import "login";
@import "sms_login";
@import "back";
@import "code";
@import "bind";
... ...
... ... @@ -20,7 +20,7 @@
text-align: left;
font-size: 16PX;
.go-register {
.sms-login {
text-decoration: underline;
color: #858585;
}
... ... @@ -82,11 +82,19 @@
background-color: #333;
border: none;
border-radius: 20PX;
margin: 0 auto;
margin: 0 auto 28px;
font-size: 16PX;
color: #d8d8d8;
}
.go-register {
color: #858585;
a {
color: inherit;
}
}
.login-tip {
font-size: 16PX;
position: relative;
... ...
.sms-login {
.sms-input {
margin-top: 60px;
}
#resend-sms {
display: block;
background-color: transparent;
width: 190px;
height: 52px;
border: 1px solid #36a74c;
border-radius: 26px;
font-size: 20px;
line-height: 1;
color: #36a74c;
}
button {
border: none;
}
}
.sms-login__msg {
font-size: 28px;
color: #fff;
margin-bottom: 20px;
.tel {
color: #41cbe7;
}
&.small {
font-size: 22px;
color: #858585;
}
}
... ...
... ... @@ -39,6 +39,11 @@ module.exports = {
path: path.join(__dirname, 'bundle'), // absolute path
filename: '[name].js'
},
resolve: {
alias: {
plugin: path.join(__dirname, 'js/plugin')
}
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.CommonsChunkPlugin({
... ...