Authored by htoooth

fix format

... ... @@ -61,7 +61,7 @@ app.use(session({
secret: '82dd7e724f2c6870472c89dfa43cf48d',
name: 'yohoblk-session',
cookie: {
domain: 'yohoblk.com',
domain: config.cookieDomain,
httpOnly: false
},
store: new MemcachedStore({
... ...
... ... @@ -11,6 +11,8 @@ const WeixinStrategy = require('passport-weixin');
const SinaStrategy = require('passport-sina').Strategy;
const LocalStrategy = require('passport-local').Strategy;
const QQStrategy = require('passport-qq').Strategy;
const DoubanStrategy = require('passport-douban').Strategy;
const RenrenStrategy = require('passport-renren').Strategy;
const AlipayStrategy = require('./models/passport-alipay').Strategy;
const md5 = require('md5');
... ... @@ -35,9 +37,9 @@ passport.use(new LocalStrategy({
let area = req.body.area || '86';
if (isNaN(parseInt(area, 0)) || _.isEmpty(username) || _.isEmpty(password)) {
if (isNaN(_.parseInt(area)) || _.isEmpty(username) || _.isEmpty(password)) {
logger.info(`【Passport Loginbad params, area:${area} account:${username} password:${password}`);
return done('登录参数错误', null);
return done({message: '登录参数错误'}, null);
}
let verifyEmail = helpers.verifyEmail(username);
... ... @@ -45,13 +47,19 @@ passport.use(new LocalStrategy({
if (!verifyEmail && !verifyMobile) {
logger.info(`【Passport Loginbad account, email:${verifyEmail} mobile:${verifyMobile}`);
return done('登录账号格式错误', null);
return done({message: '登录账号格式错误'}, null);
}
let expire = req.cookies['LE' + md5('_LOGIN_EXPIRE')];
if (_.isEmpty(expire) || expire < (new Date()).getTime() / 1000) {
return done('页面停留时间过长,请刷新页面', null);
return done({message: '页面停留时间过长,请刷新页面'}, null);
}
let verifyCode = req.body.captcha;
if (verifyCode && verifyCode !== req.session.captcha) {
return done({message: '验证码不正确或验证码过期', needCaptcha: true}, null);
}
let shoppingKey = cookie.getShoppingKey(req);
... ... @@ -66,19 +74,16 @@ passport.use(new LocalStrategy({
let cacheGet = [cache.get(errorLoginKey), cache.get(accountKey), cache.get(ipKey)];
Promise.all(cacheGet).then(times => {
let errLoginTimes = parseInt(times[0], 0) || 0;
let accountTimes = parseInt(times[1], 0) || 0;
let ipTimes = parseInt(times[2], 0) || 0;
console.log(errLoginTimes);
if (accountTimes >= 10) {
done({ message: '您的账号已被暂时锁定,请稍后再试' }, null);
} else if (ipTimes >= 100) {
done({ message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试' }, null);
let errLoginTimes = _.parseInt(times[0]) || 0;
let accountTimes = _.parseInt(times[1]) || 0;
let ipTimes = _.parseInt(times[2]) || 0;
if (accountTimes >= Infinity) {
done({message: '您的账号已被暂时锁定,请稍后再试'}, null);
} else if (ipTimes >= Infinity) {
done({message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试'}, null);
} else {
return AuthHelper.signin(area, username, password, shoppingKey).then((result) => {
console.log(result);
if (result.code && result.code === 200 && result.data.uid) {
cache.del(errorLoginKey);
... ... @@ -93,19 +98,17 @@ passport.use(new LocalStrategy({
// 再次校验
if (ipTimes >= 100) {
done({ message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试' }, null);
done({message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试'}, null);
} else if (accountTimes >= 10) {
done({ message: '您的账号已被暂时锁定,请稍后再试' }, null);
done({message: '您的账号已被暂时锁定,请稍后再试'}, null);
} else if (errLoginTimes >= 3) {
done({
message: `您输入的密码及账户名不匹配,
是否<a href="${helpers.urlFormat('/passport/back/index')}" target="_blank">忘记密码?</a>`,
message: '请输入正确的账号或密码',
needCaptcha: true
});
} else {
done({
message: `您输入的密码及账户名不匹配,
是否<a href="${helpers.urlFormat('/passport/back/index')}" target="_blank">忘记密码?</a>`,
message: '请输入正确的账号或密码',
needCaptcha: false
});
}
... ... @@ -157,7 +160,25 @@ passport.use('qq', new QQStrategy({
passport.use('alipay', new AlipayStrategy({
partner: '2088701661478015',
key: 'kcxawi9bb07mzh0aq2wcirsf9znusobw',
callbackURL: `${siteUrl}/passport/login/alipay/callback`
}), (profile, done) => {
return_url: `${siteUrl}/passport/login/alipay/callback`
}, (profile, done) => {
done(null, profile);
}));
// douban 登录
passport.use('douban', new DoubanStrategy({
clientID: '03b4e36bf13dc75a0b1eaa43d3b9560e',
clientSecret: 'f16d5913e8610672',
callbackURL: `${siteUrl}/passport/autosign/doubanback`
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
});
}));
// renren 登录
passport.use('renren', new RenrenStrategy({
clientID: '783130c654c94a77ace97054ae266019',
clientSecret: '05e430de8c1e40d3a1f39ca8d3f8252c',
callbackURL: `${siteUrl}/passport/login/renren/callback`
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}));
... ...
... ... @@ -48,7 +48,7 @@ const validateInputAPI = (req, res, next) => {
/**
* 校验用户输入信息,是否是已经注册的用户
*/
const validateUserPage = (req, res, next) => {
const validateInputPage = (req, res, next) => {
let userInput = req.body.phoneNum || '';
let areaCode = (req.body.area || '86').replace('+', '');
... ... @@ -77,8 +77,6 @@ const sendCodePage = (req, res, next) => {
service.sendCodeToUserAsync(inputInfo.type, inputInfo.phone, inputInfo.area)
.then(result => {
console.log(result);
if (!(result.code && result.code === 200)) {
return res.redirect(helpers.urlFormat('/passport/back/index'));
}
... ... @@ -100,6 +98,7 @@ const saveInSession = (req, res) => {
{
req.session.mobile = req.inputInfo.phone;
req.session.area = req.inputInfo.area;
req.session.verifyCode = req.session.captcha;
res.redirect(helpers.urlFormat('/passport/back/verification'));
break;
}
... ... @@ -140,9 +139,9 @@ const validateEmailInSession = (req, res, next) => {
return res.redirect(helpers.urlFormat('/passport/back/index'));
}
let isp = email.split('@')[1];
let isp = email.split('@')[1].toLowerCase();
const mapperEmailISP = {
'yoho.cn': 'http://smail.yoho.cn'
'yoho.cn': 'http://exmail.qq.com/login'
};
req.body.emailUrl = mapperEmailISP[isp] || `http://mail.${isp}`;
... ... @@ -256,9 +255,9 @@ const resetPwdSuccessPage = (req, res, next) => {
};
const verifyCodeByMobileAPI = (req, res) => {
let mobile = req.param('mobile', '');
let area = req.param('area', '86');
let mobileCode = req.param('code', '');
let mobile = req.body.mobile || '';
let area = req.body.area || '86';
let mobileCode = req.body.code || '';
const ERR = {
code: 400,
message: '验证码错误!',
... ... @@ -281,7 +280,6 @@ const verifyCodeByMobileAPI = (req, res) => {
};
const validateExistCodePage = (req, res, next) => {
let code = req.query.code || req.body.code;
if (!code) {
... ... @@ -345,7 +343,7 @@ const updatePwdAPI = (req, res, next) => {
const validateMobileInSession = (req, res, next) => {
req.body.mobile = req.session.mobile || '';
req.body.verifyCode = req.session.verifyCode || '';
req.body.verifyCode = req.session.captcha || '';
req.body.area = req.session.area || '';
if (!(req.body.mobile && req.body.verifyCode)) {
... ... @@ -373,7 +371,7 @@ module.exports = {
resetPwdSuccessPage, // 重设密码成功页面
validateInputAPI, // 验证用户输入的邮箱或者手机是否合法,返回是json
validateUserPage, // 验证用户输入的邮箱或者手机是否合法,跳转是页面
validateInputPage, // 验证用户输入的邮箱或者手机是否合法,跳转是页面
validateEmailInSession, // 验证邮箱是否在session中
validateMobileInSession, // 验证手机是否在session中
... ...
... ... @@ -6,8 +6,242 @@
'use strict';
const _ = require('lodash');
const helpers = global.yoho.helpers;
const PassportHelper = require('../models/passport-helper');
const BindService = require('../models/bind-service');
const AuthHelper = require('../models/auth-helper');
const UserService = require('../models/user-service');
const Sources = {
qq: 'QQ',
sina: '微博',
alipay: '支付宝',
wechat: '微信',
renren: '人人',
douban: '豆瓣'
};
const bind = {
indexPage: (req, res) => {
let openId = req.query.openId;
let sourceType = req.query.sourceType;
res.render('bind', {
thirdLogin: true,
openId: openId,
sourceType: sourceType,
region: PassportHelper.getCountry(),
serviceUrl: helpers.urlFormat('/help', {
category_id: 9
}),
module: 'passport',
page: 'bind',
title: '联合登录补全信息'
});
},
noregist: (req, res) => {
let mobile = req.body.mobile;
let sourceType = req.body.sourceType;
let openId = req.body.openId;
let area = req.body.area;
res.render('bind/noregist', {
enablePerfectInformation: true,
mobile: mobile,
sourceType: sourceType,
openId: openId,
area: area,
module: 'passport',
page: 'noregist',
title: '登录绑定'
});
},
relate: (req, res, next) => {
let mobile = req.body.mobile;
let sourceType = req.body.sourceType;
let openId = req.body.openId;
let area = req.body.area;
UserService.findByMobileAsync(area, mobile).then(user => {
if (user) {
return {
thumb: user.headImg,
userName: user.username,
loginHref: user.bindLogin
};
} else {
return {};
}
}).then(user => {
let data = _.assign(user, {
phoneNum: mobile,
areaCode: area,
openId: openId,
sourceType: sourceType,
changeHref: helpers.urlFormat('/passport/thirdlogin/index', {openId: openId, sourceType: sourceType}),
module: 'passport',
page: 'noregist',
title: '账号关联'
});
res.render('bind/relate', data);
}).catch(next);
},
bindSuccess: (req, res) => {
let sourceType = _.trim(req.query.sourceType);
let sourceInfo = sourceType.split('_');
let sourceName = Sources[sourceInfo[0]];
let isRelate = (sourceInfo[1] === 'relate');
let data = {
thirdLogin: true,
goShopping: helpers.urlFormat('/'),
sourceName: sourceName,
relate: isRelate,
module: 'passport',
page: 'bind-success',
title: '绑定手机号'
};
res.render('bind/success', data);
},
bindCheck: (req, res, next) => {
let mobile = req.body.mobile;
let openId = req.body.openId;
let area = req.body.area || '86';
let sourceType = req.body.sourceType;
if (mobile && openId && area && sourceType) {
BindService.bindCheck(mobile, openId, sourceType, area).then(result => {
if (!result || !result.code) {
return { code: 400, message: '', data: '' };
} else if (result.code === 200 && result.data.is_register === 0) {
let nextUrl = helpers.urlFormat('/passport/thirdlogin/noregist');
// 绑定流程:code=200 未注册,可绑定
return { code: 200, message: result.message, data: { next: nextUrl } };
} else if (result.code === 200 && result.data.is_register === 1) {
return PassportHelper.getUserInfo(area, mobile).then(user => {
// 绑定流程:code=201 已注册 绑定过其他第三方
return {code: 201, message: result.message, data: { user: user } };
});
} else if (result.code === 200 && result.data.is_register === 3) {
let nextUrl = helpers.urlFormat('/passport/thirdlogin/relate');
// 关联流程
return { code: 203, message: result.message, data: { next: nextUrl } };
} else if (result.code === 506 || result.code === 505) {
return PassportHelper.getUserInfo(area, mobile).then(user => {
// 绑定流程:code=201 已注册 绑定过其他第三方
return { code: 205, message: result.message, data: { user: user } };
});
} else {
return { code: result.code, message: result.message, data: result.data ? result.data : '' };
}
}).then(result => {
res.json(result);
}).catch(next);
} else {
res.json({ code: 400, message: '', data: '' });
}
},
sendBindMsg: (req, res, next) => {
let mobile = req.body.mobile;
let area = req.body.area;
BindService.sendBindMsg(area, mobile).then(result => {
if (result && result.code) {
res.json(result);
} else {
res.json({ code: 400, message: '', data: '' });
}
}).catch(next);
},
checkBindMsg: (req, res, next) => {
let mobile = req.body.mobile;
let area = req.body.area;
let code = req.body.code;
BindService.checkBindCode(area, mobile, code).then(result => {
if (result && result.code) {
res.json(result);
} else {
res.json({ code: 400, message: '', data: '' });
}
}).catch(next);
},
bindMobile: (req, res, next) => {
let mobile = _.trim(req.body.mobile);
let area = _.trim(req.body.area) || '86';
let openId = _.trim(req.body.openId);
let sourceType = _.trim(req.body.sourceType);
let code = _.trim(req.body.code);
let password = _.trim(req.body.password) || '';
BindService.checkBindCode(area, mobile, code).then(result => {
if (result && result.code !== 200) {
return { code: 402, message: '短信验证码不正确', data: '' };
} else {
return BindService.bindMobile(openId, sourceType, mobile, area, password);
}
}).then(result => {
if (result && result.code) {
if (result.code === 200 && result.data && result.data.uid) {
let refer = helpers.urlFormat('/passport/thirdlogin/bindsuccess', {
sourceType: sourceType + '_bind'
});
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return { code: 200, message: result.message, data: {refer: refer }};
});
} else {
return { code: result.code, message: result.message, data: { refer: ''} };
}
} else {
return {code: 400, message: '', data: ''};
}
}).then(result => {
res.json(result);
}).catch(next);
},
relateMobile: (req, res, next) => {
let mobile = req.body.mobile;
let openId = req.body.openId;
let areaCode = req.body.areaCode || '86';
let sourceType = req.body.sourceType;
let code = req.body.code;
if (_.isNumber(parseInt(mobile, 0)) && openId && areaCode && sourceType && code) {
BindService.checkBindCode(areaCode, mobile, code).then(result => {
if (result && result.code && result.code === 200) {
return BindService.relateMobile(openId, sourceType, mobile, code);
} else {
return { code: 402, message: '短信验证码错误', data: '' };
}
}).then(result => {
if (result && result.code) {
if (result.code === 200 && result.data && result.data.uid) {
let refer = helpers.urlFormat('/passport/thirdlogin/bindsuccess', {
sourceType: sourceType + '_bind'
});
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return { code: 200, message: result.message, data: {refer: refer }};
});
} else {
return { code: result.code, message: result.message, data: { refer: ''} };
}
} else {
return {code: 400, message: '', data: ''};
}
}).then(result => {
res.json(result);
}).catch(next);
} else {
res.json({ code: 400, message: '', data: '' });
}
}
};
module.exports = bind;
... ...
... ... @@ -4,10 +4,11 @@
'use strict';
const captchaService = require('../models/captcha-service');
const helpers = global.yoho.helpers;
const requiredAPI = (req, res, next) => {
let captchaToken = (req.body.verifyCode || '').toLowerCase();
let captchaToken = req.body.verifyCode || '';
if (captchaToken === req.session.captcha) {
return next();
... ... @@ -20,16 +21,32 @@ const requiredAPI = (req, res, next) => {
};
const requiredPage = (req, res, next) => {
let captchaToken = (req.body.verifyCode || '').toLowerCase();
let captchaToken = req.body.verifyCode || '';
if (captchaToken === req.session.captcha) {
return next();
} else {
return res.redirect(helpers.urlFormat('/passport/back/index.html'));
return res.redirect(helpers.urlFormat('/passport/back/index'));
}
};
const generate = (req, res) => {
let width = req.query.w || 150;
let height = req.query.h || 50;
let length = +(req.query.l || 4);
let captcha = captchaService.generateCaptcha(width, height, length);
req.session.captcha = captcha.text;
res.writeHead(200, {
'Content-Type': 'image/png'
});
res.end(captcha.image);
};
module.exports = {
requiredAPI,
requiredPage
requiredPage,
generate
};
... ...
... ... @@ -16,9 +16,7 @@ const config = global.yoho.config;
const cache = global.yoho.cache;
const AuthHelper = require('../models/auth-helper');
const PassportHelper = require('../models/passport-helper');
const loginPage = `${config.siteUrl}/passport/login/index`;
const SIGNIN_LEFT_BANNER_CODE = 'db350894e01e90eac55cd3a13ad77331';
const loginPage = `${config.siteUrl}/signin.html`;
// 第三方登录回调
function doPassportCallback(req, res, user) {
... ... @@ -47,7 +45,7 @@ function doPassportCallback(req, res, user) {
user.nickname, user.openId, user.sourceType, shoppingKey);
}
signinByOpenID.then((result) => {
return signinByOpenID.then((result) => {
if (result.code !== 200) {
return Promise.reject(result);
}
... ... @@ -63,9 +61,7 @@ function doPassportCallback(req, res, user) {
});
}
}).then((redirectTo) => {
res.redirect(redirectTo);
}).catch(() => {
res.redirect(loginPage);
return res.redirect(redirectTo);
});
} else {
res.redirect(loginPage);
... ... @@ -93,7 +89,6 @@ const common = {
cache.get(errorLoginKey).then(errloginTimes => {
errloginTimes = parseInt(errloginTimes, 0) || 0;
console.log(errloginTimes);
if (!isNaN(errloginTimes) && errloginTimes >= 3) {
result.data = {needCaptcha: true};
}
... ... @@ -122,29 +117,26 @@ const local = {
areaName = area ? area.name : '';
}
PassportHelper.getLeftBannerAsync(SIGNIN_LEFT_BANNER_CODE).then(cover => {
res.render('login', {
loginPage: true,
passport: {
coverHref: cover.url,
coverImg: cover.img,
countryCode: bindArea,
countryName: areaName,
countryList: areaArr,
forgetPwd: helpers.urlFormat('/passport/back/index'),
fastReg: helpers.urlFormat('/reg.html'),
weixinLogin: helpers.urlFormat('/passport/autosign/wechat'),
qqLogin: helpers.urlFormat('/passport/autosign/qq'),
weiboLogin: helpers.urlFormat('/passport/autosign/sina'),
alipayLogin: helpers.urlFormat('/passport/autosign/alipay'),
doubanLogin: helpers.urlFormat('/passport/autosign/douban'),
renrenLogin: helpers.urlFormat('/passport/autosign/renren'),
bindMobile: bindMobile
},
module: 'passport',
page: 'login',
title: '用户登录'
});
res.render('login', {
loginPage: true,
passport: {
countryCode: bindArea,
countryName: areaName,
countryList: areaArr,
forgetPwd: helpers.urlFormat('/passport/back/index'),
fastReg: helpers.urlFormat('/passport/reg/index'),
weixinLogin: helpers.urlFormat('/passport/autosign/wechat'),
qqLogin: helpers.urlFormat('/passport/autosign/qq'),
weiboLogin: helpers.urlFormat('/passport/autosign/sina'),
alipayLogin: helpers.urlFormat('/passport/autosign/alipay'),
doubanLogin: helpers.urlFormat('/passport/autosign/douban'),
renrenLogin: helpers.urlFormat('/passport/autosign/renren'),
bindMobile: bindMobile
},
module: 'passport',
page: 'login',
title: '用户登录'
});
},
login: (req, res, next) => {
... ... @@ -222,15 +214,15 @@ const wechat = {
if (err) {
log.error(`wechat authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
} else {
doPassportCallback(req, res, {
openId: user._json.openid,
unionId: user._json.unionid || user.id,
nickname: user._json.nickname || user.displayName,
sourceType: 'wechat',
rawUser: user
}).catch(next);
}
doPassportCallback(req, res, {
openId: user._json.openid,
unionId: user._json.unionid || user.id,
nickname: user._json.nickname || user.displayName,
sourceType: 'wechat',
rawUser: user
}).catch(next);
})(req, res, next);
} else {
return next(new Error('Auth State Mismatch'));
... ... @@ -284,7 +276,7 @@ const qq = {
return res.redirect(loginPage);
}
let nickname = user.nickname;
let openId = user.openid;
let openId = user.id;
doPassportCallback(req, res, {
openId: openId,
... ... @@ -320,11 +312,67 @@ const alipay = {
}
};
const douban = {
login: (req, res, next) => {
req.session = req.session || {};
req.session.authState = uuid.v4();
return passport.authenticate('douban', {
state: req.session.authState
})(req, res, next);
},
callback: (req, res, next) => {
passport.authenticate('douban', (err, user) => {
if (err) {
log.error(`douban authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.displayName;
let openId = user.id;
doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'douban'
}).catch(next);
})(req, res, next);
}
};
const renren = {
login: (req, res, next) => {
req.session = req.session || {};
req.session.authState = uuid.v4();
return passport.authenticate('renren', {
state: req.session.authState
})(req, res, next);
},
callback: (req, res, next) => {
passport.authenticate('renren', (err, user) => {
if (err) {
log.error(`renren authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.displayName;
let openId = user.id;
doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'renren'
}).catch(next);
})(req, res, next);
}
};
module.exports = {
common: common,
wechat: wechat,
local: local,
sina: sina,
qq: qq,
alipay: alipay
alipay: alipay,
douban: douban,
renren: renren
};
... ...
/**
* 注册控制器
*/
* 注册控制器
*/
'use strict';
const _ = require('lodash');
const Promise = require('bluebird');
const passportHelper = require('../models/passport-helper');
const regService = require('../models/reg-service');
const userService = require('../models/user-service');
const authHelper = require('../models/auth-helper');
const config = require('../../../config/common');
let helpers = global.yoho.helpers;
let cache = global.yoho.cache;
let cookie = global.yoho.cookie;
let config = global.yoho.config;
let index = (req, res, next) => {
/**
* 检查图形验证码
*/
let checkCode = (req, res, next) => {
let verifyCode = _.trim(req.body.verifyCode);
if (verifyCode !== req.session.captcha) {
return res.json({
code: 400,
message: '验证码错误'
});
}
next();
};
/**
* 检查手机格式
*/
let checkMobileMiddleware = (req, res, next) => {
let mobile = +req.body.mobile;
let area = +req.body.area;
if (!_.isNumber(mobile) || !_.isNumber(area)) {
return res.json({
code: 400,
message: '手机号码格式不正确'
});
}
next();
};
/**
* 检查密码格式
*/
let checkPassword = (req, res, next) => {
/* 判断密码是否符合规则 */
let password = req.body.password;
if (!helpers.verifyPassword(password)) {
return res.json({
code: 400,
message: '密码不正确'
});
}
next();
};
/**
* 注册首页
*/
let index = (req, res) => {
// 设置注册有效时间30分钟, 防机器刷
req.session._REG_EXPIRE = Date.now() + 1800000;
... ... @@ -23,24 +74,23 @@ let index = (req, res, next) => {
domain: config.cookieDomain
});
regService.getRegData().then((result) => {
res.render('reg/index', {
title: '新用户注册',
passport: {
region: passportHelper.getCountry(),
location: '+86',
captchaUrl: helpers.urlFormat('/passport/images', {t: Date.now()}),
itemUrl: helpers.urlFormat('/help/', {category_id: 9}),
referUrl: refer,
loginUrl: helpers.urlFormat('/signin.html', {refer: refer}),
coverHref: result.url,
coverImg: result.img,
regBtnText: '立即注册'
}
});
}).catch(next);
res.render('reg/index', {
title: '新用户注册',
passport: {
region: passportHelper.getCountry(),
location: '+86',
captchaUrl: helpers.urlFormat('/passport/images', {t: Date.now()}),
itemUrl: helpers.urlFormat('/help/', {category_id: 9}),
referUrl: refer,
loginUrl: helpers.urlFormat('/signin', {refer: refer}),
regBtnText: '立即注册'
}
});
};
/**
* 检查手机号码是否注册过
*/
let checkMobile = (req, res, next) => {
let data = {
code: 400
... ... @@ -48,24 +98,22 @@ let checkMobile = (req, res, next) => {
let mobile = +req.body.mobile;
let area = +req.body.area;
if (!_.isNumber(mobile) || !_.isNumber(area)) {
data.message = '手机号码格式不正确';
return res.json(data);
}
// 判断手机号是否检查超过指定次数
let key = 'checkmobilenum_' + passportHelper.makeAreaMobile(area, mobile);
let regCheckKey = 'regCheckMobileNum_' + passportHelper.makeAreaMobile(area, mobile);
cache.get(key).then((checkNum) => {
cache.get(regCheckKey).then((checkNum) => {
checkNum = +(checkNum || 0);
cache.set(key, ++checkNum).catch(next);
return cache.set(regCheckKey, ++checkNum).then(() => {
return checkNum;
});
}).then((checkNum) => {
if (checkNum > 500) {
data.message = '检查次数太多';
return res.json(data);
}
// 判断用户是否存在
// 判断用户是否存在
return userService.findByMobileAsync(area, mobile).then((user) => {
if (!_.isEmpty(user)) {
data.message = '手机号码已经存在';
... ... @@ -78,106 +126,76 @@ let checkMobile = (req, res, next) => {
}).catch(next);
};
let picCaptcha = (req, res, next) => {
let verifyCode = _.trim(req.body.verifyCode);
let picFlag = true; // TODO: 图形验证码校验
if (picFlag) {
return res.json({
code: 200,
message: '验证码正确'
});
}
/**
* 图形验证码校验
*/
let picCaptcha = (req, res) => {
// 图形验证码已经在中间件校验,如果走到这儿,说明通过校验
return res.json({
code: 400,
message: '验证码错误'
code: 200,
message: '验证码正确'
});
};
/**
* 发送验证码
*/
let sendBindMsg = (req, res, next) => {
let data = {
code: 400,
message: '',
data: ''
};
let mobile = +req.body.mobile;
let area = +req.body.area;
let verifyCode = +req.body.verifyCode;
// 判断参数是否合法
if (!_.isNumber(mobile) || !_.isNumber(area)) {
data.message = '手机号码格式不正确';
return res.json(data);
}
Promise.coroutine(function*() {
let data = {
code: 400,
message: '',
data: ''
};
// 检查是否检查过
// let makeMobile = passportHelper.makeAreaMobile(area, mobile);
//
// if (req.session[`checkmobile_${makeMobile}`] !== makeMobile) {
// data.message = '发送失败';
// return res.json(data);
// }
let mobile = +req.body.mobile;
let area = +req.body.area;
// 校验是否发送过多
let sendCodeKey = `send_code_${area}_${mobile}`;
// 检查是否检查过
let checkNum = yield cache.get(`regCheckMobileNum_${passportHelper.makeAreaMobile(area, mobile)}`);
cache.get(sendCodeKey).then((sendCodeTimes) => {
if (!sendCodeTimes) {
sendCodeTimes = 0;
} else {
sendCodeTimes = +sendCodeTimes;
}
if (sendCodeTimes >= 10) {
data.message = '您已多次提交验证码,请尽快联系客服解决';
if (!checkNum) {
data.message = '发送失败';
return res.json(data);
}
// 校验是否发送过多
let sendCodeKey = `send_code_${area}_${mobile}`;
let sendCodeTimes = yield cache.get(sendCodeKey);
sendCodeTimes = +(sendCodeTimes || 0);
if (sendCodeTimes >= 5) {
data.message = '您收到的验证码短信已超过本日限定最多次数,请您耐心等待';
data.message = sendCodeTimes >= 10 ? '您已多次提交验证码,请尽快联系客服解决' : '您收到的验证码短信已超过本日限定最多次数,请您耐心等待';
return res.json(data);
}
// TODO: 检测验证码不正确
// if (!PassportModel::verifyCode($verifyCode)) {
// $data['code'] = 400;
// $data['message'] = '图形验证码不正确';
// break;
// }
/* 向手机发送注册验证码 */
return regService.sendCodeToMobile(area, mobile).then((result) => {
return cache.set(sendCodeKey, sendCodeTimes + 1, 3600).then(() => {
if (result.code) {
return res.json(result);
} else {
data.message = '发送失败';
return res.json(data);
}
});
let result = yield regService.sendCodeToMobile(area, mobile);
return cache.set(sendCodeKey, sendCodeTimes + 1, 3600).then(() => {
if (result.code) {
return res.json(result);
} else {
data.message = '发送失败';
return res.json(data);
}
});
}).catch(next);
})().catch(next);
};
/**
* 短信验证码校验
*/
let msgCaptcha = (req, res, next) => {
let data = {
code: 400,
message: '',
data: ''
};
let area = +req.body.area;
let mobile = +req.body.mobile;
let code = +req.body.code; // 短信验证码
// 判断参数是否合法
if (!_.isNumber(mobile) || !_.isNumber(area)) {
data.message = '手机号码格式不正确';
return res.json(data);
}
regService.validMobileCode(area, mobile, code).then((result) => {
if (result.code) {
return res.json(result);
... ... @@ -188,51 +206,29 @@ let msgCaptcha = (req, res, next) => {
}).catch(next);
};
/**
* 注册接口
*/
let mobileRegister = (req, res, next) => {
let data = {
code: 400,
message: '',
data: ''
};
let area = +req.body.area;
let mobile = +req.body.mobile;
// 判断参数是否合法
if (!_.isNumber(mobile) || !_.isNumber(area)) {
data.message = '手机号码格式不正确';
return res.json(data);
}
/* 判断是否是有效的注册方式,防注册机刷 */
let regExpireTime = req.session._REG_EXPIRE;
if (!regExpireTime || regExpireTime < Date.now()) {
data.message = '注册超时';
return res.json(data);
}
// TODO: 检测验证码不正确
// $verifyCode = strtolower(trim($this->post('verifyCode'))); //图形验证码
// if (!PassportModel::verifyCode($verifyCode)) {
// $data['message'] = '验证码不正确';
// break;
// }
/* 判断密码是否符合规则 */
let code = +req.body.code; // 短信验证码
let password = req.body.password;
if (!helpers.verifyPassword(password)) {
data.message = '密码不正确';
return res.json(data);
}
Promise.coroutine(function*() {
let data = {
code: 400,
message: '',
data: ''
};
/* 判断是否是有效的注册方式,防注册机刷 */
let regExpireTime = req.session._REG_EXPIRE;
if (!regExpireTime || regExpireTime < Date.now()) {
data.message = '注册超时';
return res.json(data);
}
/* IP仅允许点击注册500次/时 */
let ip = req.ip;
let ipKey = 'ip_register_' + ip;
/* IP仅允许点击注册500次/时 */
let ipKey = 'ip_register_' + req.ip;
let ipTimes = yield cache.get(ipKey);
cache.get(ipKey).then((ipTimes) => {
if (!ipTimes) {
ipTimes = 0;
} else {
... ... @@ -244,72 +240,61 @@ let mobileRegister = (req, res, next) => {
return res.json(data);
}
return cache.set(ipKey, ipTimes + 1, 3600).then(() => {
/* 验证注册的标识码是否有效 */
return regService.validMobileCode(area, mobile, code).then((result) => {
if (!result.code || result.code !== 200) {
data.message = '验证码错误';
return res.json(data);
}
yield cache.set(ipKey, ipTimes + 1, 3600);
let shoppingKey = cookie.getShoppingKey(req);
/* 手机注册: 调用注册接口,ip限制计数 */
return regService.regMobile(area, mobile, password, shoppingKey).then((regResult) => {
if (!regResult.code || regResult.code !== 200) {
data.message = '注册失败';
return res.json(data);
}
// 返回跳转到来源页面
let refer = req.cookies.refer;
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
refer = '/?go=1';
}
if (/sign|login/.test(refer)) {
refer = '/?go=1';
}
return authHelper.syncUserSession(regResult.data.uid, req, res).then(() => {
return res.json({
code: 200,
message: '注册成功',
data: {
href: helpers.urlFormat('/passport/reg/success', {
next: refer,
goShoppingUrl: config.siteUrl
})
}
});
});
});
let area = +req.body.area;
let mobile = +req.body.mobile;
let code = +req.body.code; // 短信验证码
let password = req.body.password;
let result = yield regService.validMobileCode(area, mobile, code); // 验证注册的标识码是否有效
if (!result.code || result.code !== 200) {
data.message = '验证码错误';
return res.json(data);
}
/* 手机注册: 调用注册接口*/
let regResult = yield regService.regMobile(area, mobile, password, cookie.getShoppingKey(req));
if (!regResult.code || regResult.code !== 200) {
data.message = '注册失败';
return res.json(data);
}
return authHelper.syncUserSession(regResult.data.uid, req, res).then(() => {
return res.json({
code: 200,
message: '注册成功',
data: {
href: helpers.urlFormat('/passport/reg/success', {
next: cookie.getRefer(req, '/?go=1'),
goShoppingUrl: config.siteUrl
})
}
});
});
}).catch(next);
})().catch(next);
};
let success = (req, res, next) => {
let success = (req, res) => {
let goUrl = req.query.next || config.siteUrl;
let goShoppingUrl = req.query.goShoppingUrl || config.siteUrl;
regService.getRegData().then((result) => {
res.render('reg/success', {
title: '注册成功',
passport: {
goUrl: goUrl,
goShoppong: goShoppingUrl,
coverHref: result.url,
coverImg: result.img
}
});
}).catch(next);
let mobile = req.query.mobile || '用户';
res.render('reg/success', {
title: '注册成功',
passport: {
goUrl: goUrl,
goShoppong: goShoppingUrl,
mobile: mobile
}
});
};
module.exports = {
checkCode,
checkMobileMiddleware,
checkPassword,
index,
success,
checkMobile,
... ...
... ... @@ -43,10 +43,13 @@ function paramsToRaw(params) {
function AlipayStrategy(options, verify) {
if (typeof options === 'function') {
verify = options;
options = {};
}
options = options || {};
passport.Strategy.call(this);
this.name = 'alipay';
this._verify = verify;
this._options = options;
}
util.inherits(AlipayStrategy, passport.Strategy);
... ... @@ -60,32 +63,34 @@ AlipayStrategy.prototype.authenticate = function(req, options) {
delete query.sign_type;
delete query.sign;
let signString = paramsToRaw(query) + options.key;
let signString = paramsToRaw(query) + this._options.key;
if (signType === 'MD5' && sign !== md5(signString)) {
this.error('alipay callback sign check fail');
this.fail('alipay callback sign check fail');
return;
}
if (req.query.is_success === 'T') {
let user = {
userId: req.query.user_id,
realName: req.query.realName,
realName: req.query.real_name,
email: req.query.email
};
this.success(user, null);
} else {
this.error('alipay login fail');
this.fail(req.error_code);
}
} else {
let params = _.assign(defaultOptions, options);
let params = _.assign(defaultOptions, this._options, options);
let signType = params.sign_type;
let key = params.key;
delete params.sign_type;
delete params.sign;
let signString = paramsToRaw(params) + options.key;
delete params.key;
let signString = paramsToRaw(params) + key;
if (signType === 'MD5') {
params.sign = md5(signString);
... ...
... ... @@ -9,6 +9,9 @@ const _ = require('lodash');
const helpers = global.yoho.helpers;
const indexService = require('./index-service');
const UserService = require('./user-service');
const DEFAULT_HEAD_IMG_ICO = 'http://img10.static.yhbimg.com/headimg/2013/11/28/09/01cae078abe5fe320c88cdf4c220212688.gif?imageView/2/w/100/h/100';
/**
* 获得图片
... ... @@ -20,7 +23,7 @@ const getLeftBannerAsync = (resourceCode) => {
url: ''
};
return co(function * () {
return co(function* () {
let resource = yield indexService.getResourceAsync(resourceCode);
if (_.isEmpty(resource)) {
... ... @@ -239,6 +242,26 @@ const isPassword = pwd => {
return pwdRegexp.test(_.trim(pwd));
};
/**
* 第三方登录 根据手机号获取用户相关信息
*/
const getUserInfo = (area, mobile) => {
return UserService.findByMobileAsync(area, mobile).then(user => {
let profile = (user.profile_name || mobile).toString();
if ((profile.length === 11 && profile.indexOf('*') < 0) || (profile.indexOf('-') >= 0 &&
profile.indexOf('*') < 0)) {
profile = profile.substring(0, 3) + '****' + profile.substring(7, 11);
}
return {
username: profile,
headImg: user.head_ico ? helpers.image(user.head_ico, 100, 100, 2) : DEFAULT_HEAD_IMG_ICO,
bindLogin: helpers.urlFormat('/signin.html', { bindMobile: mobile, bindArea: area })
};
});
};
module.exports = {
validator: {
verifyMobile,
... ... @@ -248,5 +271,6 @@ module.exports = {
},
makeAreaMobile,
getCountry,
getLeftBannerAsync
getLeftBannerAsync,
getUserInfo
};
... ...
... ... @@ -10,15 +10,17 @@ const express = require('express');
const cRoot = './controllers';
const login = require(cRoot + '/login');
// const captcha = require(cRoot + '/captcha');
const captcha = require(cRoot + '/captcha');
const back = require(cRoot + '/back');
const reg = require(cRoot + '/reg');
const bind = require(cRoot + '/bind');
const router = express.Router(); // eslint-disable-line
// 本地登录
router.get('/login', login.common.beforeLogin, login.local.loginPage);
router.post('/login/auth', login.local.login);
router.post('/login/user', back.validateInputAPI, back.getUserInfoAPI);
router.get('/logout', login.local.logout);
// 微信登录
... ... @@ -37,18 +39,35 @@ router.get('/login/qq/callback', login.qq.callback);
router.get('/autosign/alipay', login.common.beforeLogin, login.alipay.login);
router.get('/login/alipay/callback', login.alipay.callback);
// douban登录
router.get('/autosign/douban', login.common.beforeLogin, login.douban.login);
router.get('/autosign/doubanback', login.douban.callback);
// renren登录
router.get('/autosign/renren', login.common.beforeLogin, login.renren.login);
router.get('/login/renren/callback', login.renren.callback);
router.get('/login/account', login.common.needCaptcha);
// 第三方登录后绑定
router.get('/thirdlogin/index', bind.indexPage);
router.get('/thirdlogin/bindsuccess', bind.bindSuccess);
router.post('/thirdlogin/noregist', bind.noregist);
router.post('/autouserinfo/bindCheck', bind.bindCheck);
router.post('/autouserinfo/sendBindMsg', bind.sendBindMsg);
router.post('/autouserinfo/checkBindMsg', bind.checkBindMsg);
router.post('/autouserinfo/bindMobile', bind.bindMobile);
/**
* 注册页面路由
*/
router.get('/reg/index', reg.index);
router.post('/reg/checkmobile', reg.checkMobile);
router.post('/reg/piccaptcha', reg.picCaptcha);
router.post('/reg/msgcaptcha', reg.msgCaptcha);
router.post('/reg/sendBindMsg', reg.sendBindMsg);
router.post('/reg/mobileregister', reg.mobileRegister);
router.post('/reg/checkmobile', reg.checkMobileMiddleware, reg.checkMobile);
router.post('/reg/piccaptcha', reg.checkCode, reg.picCaptcha);
router.post('/reg/msgcaptcha', reg.checkMobileMiddleware, reg.msgCaptcha);
router.post('/reg/sendBindMsg', reg.checkMobileMiddleware, reg.checkCode, reg.sendBindMsg);
router.post('/reg/mobileregister', reg.checkMobileMiddleware, reg.checkPassword, reg.checkCode, reg.mobileRegister);
router.get('/reg/success', reg.success);
/**
... ... @@ -59,24 +78,21 @@ router.get('/back/index', back.index);
// 实时验证输入是否正确
router.post('/back/authcode',
// captcha.requiredAPI,
captcha.requiredAPI,
back.validateInputAPI,
back.getUserInfoAPI);
// 提交按钮邮件API
router.post('/back/email',
// captcha.requiredPage,
back.validateUserPage,
captcha.requiredPage,
back.validateInputPage,
back.sendCodePage,
back.saveInSession);
// 提交按钮手机API
router.post('/back/mobile',
// captcha.requiredPage,
back.validateUserPage,
captcha.requiredPage,
back.validateInputPage,
back.sendCodePage,
back.saveInSession);
... ... @@ -94,28 +110,24 @@ router.get('/back/sendEmail',
// 验证手机短信页面
router.get('/back/verification',
back.validateMobileInSession,
// captcha.requiredPage,
back.verifyCodeByMobilePage);
// 重新发送短信接口
router.post('/back/sendBackMobile',
// captcha.requiredAPI,
captcha.requiredAPI,
back.validateMobileAPI,
back.sendBackMobileAPI);
// 验证手机验证码接口
router.post('/back/backMobile',
// captcha.requiredAPI,
captcha.requiredAPI,
back.verifyCodeByMobileAPI);
/**
* 重置密码
*/
// 重置密码页面
// 重置密码页面
router.get('/back/backcode',
back.validateExistCodePage,
back.validateCodeByMobilePage,
... ... @@ -134,4 +146,6 @@ router.get('/back/resetSuccess',
back.validateSuccessStatusPage,
back.resetPwdSuccessPage);
router.get('/images', captcha.generate);
module.exports = router;
... ...
<div class="bindwrapper">
<h3 class="welcomeword">
欢迎加入
<span class="yoho">YOHO!FAMILY</span>
</h3>
<p class="safeword">为了您的账户安全,请您完善账户信息,以便为您提供更优质的服务</p>
<form action="" id="bindmobileform" method="post">
<input type="hidden" value="{{openId}}" id="openId" name="openId"/>
<input type="hidden" value="{{sourceType}}" id="sourceType" name="sourceType"/>
<input type="hidden" value="86" id="areacode" name="area"/>
<div class="yohobindrow">
<div class="name areatag">地区</div>
<div class="content">
<div class="yohoselectarea">
<div class="optionshow">
<span class="areaname" id="areaname">中国</span>
<span class="righttag"></span>
</div>
<div class="optionslist hide">
{{#each region}}
<div class="optionitem" areanum="{{areaCode}}">{{name}}</div>
{{/each}}
</div>
</div>
</div>
</div>
<div class="yohobindrow">
<div class="name phonetag">手机号码</div>
<div class="content">
<div class="yohophonewrapper">
<div class="areanum" >
+
<span id="areanum">86</span>
</div>
<input type="text" class="phonenum" name="mobile" id="mobile"/>
<span class="err-tip phone-err-tip hide">
<i></i>
<em></em>
</span>
</div>
</div>
</div>
</form>
<div class="protoctolwrapper">
<div class="choosewrapper">
<input type="checkbox" class="choosetag" checked='checked'></div>
<span>
我已阅读并同意遵守
<a target="_blank" href="{{serviceUrl}}" class="protoctol">YOHO!BUY 有货服务条款</a>
</span>
</div>
<div class="btnwrapper">
<a href="javascript:void(0)" class="yohobindbtn" id="bindfirststep">下一步</a>
</div>
</div>
<div id="bindconfirm" style="display:none">
<div class="mask">
<div class="bindconfrimwrapper">
<div class="topwrapper">
<img src="" alt="" class="userphoto" id="userphoto1">
<span class="username">
用户名: <i id="username1"></span>
</div>
<h2 class="usertaginfo">
手机号
<span id="registphonetwo"></span>
已经被以上账户注册,请确认是否归您本人所有
</h2>
<h1 class="usertagremind">您可以使用该手机号码直接登录或更换一个新的手机号码绑定该账号</h1>
<a href="javascript:;" class="yohobindbtn otherphone" id="yohobindbtn">绑定其他手机号</a>
<a href="" class="logindirectly" id="logindirectly">使用手机号直接登录</a>
</div>
</div>
</div>
<div class="backdrop" style="display:none"></div>
</div>
<div id="alreayregist" style="display:none">
<div class="mask">
<div class="bindconfrimwrapper">
<div class="topwrapper">
<img src="" alt="" class="userphoto" id="userphoto">
<span class="username">
用户名: <i id="username"></i>
</span>
</div>
<h2 class="usertaginfo">
手机号
<span id="registphone"></span>
已经被以上账户注册,请确认是否归您本人所有
</h2>
<h1 class="usertagremind">您可以绑定该手机号码或更换一个新的手机号码绑定该账号,也可以使用该手机号码直接登录</h1>
<form id="gobindform" action="/passport/autouserinfo/bindMobile" method="post">
<div class="gobindwrapper">
<div class="validaterow">
<div class="content">
<input type="text" class="validatacode" placeholder="验证码" maxlength="4" id="validatenum"/>
</div>
<div class="validatewrapper">
<a href="javascript:void(0)" class="yohobindbtn" id="sendmessage">免费获取短信验证码</a>
</div>
<div class="hide" id="nopermissionmessage">
<span class="second">60</span>
秒后可重新操作
</div>
</div>
<input type="hidden" name="area" value="" id="inarea"/>
<input type="hidden" name="mobile" value="" id="inmobile"/>
<input type="hidden" name="openId" value="{{openId}}"/>
<input type="hidden" name="sourceType" value="{{sourceType}}"/>
<a href="javascript:;" class="yohobindbtn myphone" id="gotobindphone">是的我绑定该手机号</a>
</div>
<div class="gobindbottomwrapper">
<a href="javascript:;" class="logindirectly" id="yohobindbtn2">绑定其他手机号</a>
<a href="javascript:;" class="logindirectly" id="logindirectly2">使用手机号直接登录</a>
</div>
</form>
</div>
</div>
<div class="backdrop" style="display:none"></div>
</div>
\ No newline at end of file
... ...
<div class="bindwrapper">
<h3 class="welcomeword">
欢迎加入
<span class="yoho">YOHO!FAMILY</span>
</h3>
<p class="safeword">为了您的账户安全,请您完善账户信息,以便为您提供更优质的服务</p>
<p class="sendnotify">
验证码已发送至
<span class="sendphone">{{mobile}}</span>
</p>
<form action="infoform" method="post">
<div class="validaterow">
<div class="name phonetag">短信验证码</div>
<div class="content">
<div class="yohophonewrapper">
<input type="text" class="validatenum phonenum" value="" id="validatenum" maxlength="4"/>
<span class="err-tip code-err-tip hide">
<i></i>
<em></em>
</span>
</div>
</div>
<div class="validatewrapper">
<a href="javascript:void(0)" class="yohobindbtn" id="sendmessage">免费获取短信验证码</a>
</div>
<div class="hide" id="nopermissionmessage">
<span class="second">60</span>
秒后可重新操作
</div>
</div>
<div class="setpwdcontent">
<div class="yohobindrow setpwdwrapper">
<div class="name setpwdtag">设置密码</div>
<div class="content">
<div class="yohophonewrapper">
<input type="password" class="phonenum pwdcontent" maxlength="20" id="pwd" name="password" />
<span class="err-tip pwd-err-tip hide">
<i></i>
<em></em>
</span>
<div id="pwd-tips" class="hide pwd-tips">
<div class="default" id="pwd-tip1"> <i></i>
密码只支持6-20位字符
</div>
<div class="default" id="pwd-tip2"> <i></i>
由字母、 数字组合,不能包含特殊符号
</div>
</div>
</div>
</div>
</div>
<div class="safelevel clearfix">
安全程度: &nbsp;
<span class="low pwd-intensity"></span>
<span class="mid pwd-intensity"></span>
<span class="high pwd-intensity"></span>
</div>
</div>
<div class="btnwrapper confirmwrapper">
<a href="javascript:void(0)" class="yohobindbtn" id="confirmsubmit" >确定</a>
</div>
<input type="hidden" value="{{openId}}" name="openId" id="openId">
<input type="hidden" value="{{sourceType}}" name="sourceType" id="sourceType">
<input type="hidden" value="{{mobile}}" id="mobile" name="mobile"/>
<input type="hidden" value="{{area}}" id="area"/>
</form>
</div>
... ...
{{> layout/header}}
<div class="third-relate-page passport-page yoho-page clearfix">
{{# relate}}
<div id="relate-main" class="relate-main">
<div class="user-wrapper">
<div class="user-thumb">
<img src="{{thumb}}">
</div>
<label>用户名: {{userName}}</label>
</div>
<div class="remind-info">
<h3>手机号
<span>{{phoneNum}}</span>
已经被以上账户注册,点击
<a href="{{loginHref}}" target="_blank">登录该账号</a>
</h3>
<p>您可以使用此号码进行账户关联或更换一个新的手机号码</p>
</div>
<div class="option-btn">
<a id="next-step-btn">关联此手机号</a>
<a href="{{changeHref}}">更换新手机号</a>
</div>
<p class="remind-tips">注:关联的手机号码不能用来登录此账户</p>
</div>
<div id="code-validate" class="code-validate hide">
<div class="action-link">短信验证码已发送至<span>{{phoneNum}}</span></div>
<div class="validate-wrapper action-link">
<div class="code-name">短信验证码</div>
<input type="text" class="msg-code">
<label class="get-code">免费获取短信验证码</label>
<label class="count-down"><span>60</span>秒后可重新操作</label>
<div class="code-error hide">
<i></i>
验证码错误
</div>
</div>
<div class="action-link">
<button id="relate-btn" class="relate-btn">确定</button>
</div>
<p class="remind-tips">
<span class="left">注:关联的手机号码不能用来登录此账户</span>
<a class="perv-step right">返回上一步</a>
</p>
</div>
<div id="hide-info" data-mobile="{{phoneNum}}" data-area="{{areaCode}}" data-openid="{{openId}}" data-source="{{sourceType}}"></div>
{{/ relate}}
</div>
{{> layout/footer}}
\ No newline at end of file
... ...
<div class="bindsuccesswrapper">
<div class="successwrapper">
<span class="successtag"></span>
<span class="congratulation">恭喜!</span>
{{#if relate}}
<span class="successnotify">您的手机号码已经关联成功</span>
{{^}}
<span class="successnotify">您的号码已经绑定成功</span>
{{/if}}
</div>
{{#if relate}}
<h3 class="info">该手机号码不能用来登录此账户,您可以选择继续使用{{sourceName}}登录</h3>
{{^}}
<h3 class="info">今后您可以选择{{sourceName}}或绑定的手机号码+密码的方式登录此账户</h3>
{{/if}}
<a href="{{goShopping}}" class="yohobindbtn gobuynow">立即购物</a>
</div>
\ No newline at end of file
... ...
<div class="login-page passport-page yoho-page clearfix">
{{> sign-header}}
<div class="login-page passport-page yoho-page clearfix center-content">
{{# passport}}
{{> login/cover}}
<div class="content">
<ul class="login-ul">
<li class="relative clearfix">
<h2 class="title">会员登录</h2>
<span id="country-code" class="country-code right">
<em>{{countryName}} {{countryCode}}</em>
<i class="iconfont">&#xe600;</i>
<ul id="country-list" class="country-list">
{{#each countryList}}
<li data-cc="{{areaCode}}" {{# selected}}selected{{/selected}}>{{name}} {{areaCode}}</li>
{{/each}}
</ul>
</span>
</li>
<li class="relative">
<input id="account" class="account input va" name="account" value="{{bindMobile}}" type="text" placeholder="邮箱/手机号码" autocomplete="off">
<span class="err-tip hide">
<i></i>
<em></em>
</span>
</li>
<li class="relative">
<input id="password" class="password input va" name="password" type="password" placeholder="密码" autocomplete="off" maxlength="20">
<span id="caps-lock" class="caps-lock hide">大写状态开启</span>
<span class="err-tip hide">
<i></i>
<ul>
<li class="clearfix">
<h2 class="title">登录 SIGN IN</h2>
</li>
<li class="clearfix">
<select id="country-list" class="country-list">
{{#each countryList}}
<option {{#if selected}}selected="selected"{{/if}} value="{{areaCode}}">{{name}}</option>
{{/each}}
</select>
<div id="phone" class="left phone">
<span id="country-code" class="country-code">{{countryCode}}</span>
<input id="account" class="account input phone-num va" name="account" value="{{bindMobile}}"
type="text"
placeholder="Phone Number" autocomplete="off">
</div>
<span class="err-tip hide">
<span class="iconfont">&#xe60c;</span>
<em></em>
</span>
</li>
<li class="clearfix">
<input id="password" class="input password va" name="password" type="password" placeholder="Password"
autocomplete="off" maxlength="20">
<span id="caps-lock" class="caps-lock hide">大写状态开启</span>
<span class="err-tip hide">
<span class="iconfont">&#xe60c;</span>
<em>请输入密码</em>
</span>
</li>
<li class="clearfix captcha-wrap hide">
<input id="captcha" class="input va captcha" type="text" name="captcha" placeholder="图形验证码" autocomplete="off" maxlength="4">
<img id="captcha-img" class="captcha-img" alt="">
<a class="link change-captcha">换一张</a>
<span class="err-tip hide">
<i></i>
</span>
</li>
<li class="clearfix captcha-wrap hide">
<input id="captcha" class="input va captcha" type="text" name="captcha" placeholder="图形验证码"
autocomplete="off" maxlength="4">
<div class="left captcha-component">
<img id="captcha-img" class="left captcha-img" alt="">
<a class="left link change-captcha"><span class="iconfont gray">&#xe613;</span></a>
</div>
<span class="err-tip hide">
<span class="iconfont">&#xe60c;</span>
<em></em>
</span>
</li>
<li class="clearfix relative">
<span class="left login-fail-tip hide">
<span class="iconfont">&#xe608;</span>
<em></em>
</span>
<span id="login-btn" class="btn login-btn">登录</span>
</li>
<li class="clearfix">
<span class="remember-me checked">
<span class="iconfont checkbox small">&#xe602;</span>
下次自动登录
</span>
</li>
<li>
<span id="login-btn" class="login-btn btn">登录</span>
</li>
<li class="other-opts">
<span class="remember-me">
<i class="iconfont">&#xe613;</i>
记住登录状态
</span>
<span class="right">
<a class="forget-password" href="{{forgetPwd}}">忘记密码?</a>
|
<a class="fast-reg" href="{{fastReg}}">快速注册</a>
<a class="blue forget-password" href="{{forgetPwd}}">忘记密码</a>
</span>
</li>
<li class="third-party-login">
<a href="{{weixinLogin}}">
<span class="icon weixin"></span>
</a>
<a href="{{qqLogin}}">
<span class="icon qq"></span>
</a>
<a href="{{weiboLogin}}">
<span class="icon weibo"></span>
</a>
<a href="{{alipayLogin}}">
<span class="icon alipay"></span>
</a>
<a href="{{doubanLogin}}">
<span class="icon douban"></span>
</a>
<a href="{{renrenLogin}}">
<span class="icon renren"></span>
</a>
</li>
<li class="clearfix other-opts">
<span>
还没有加入YOHO!FAMILY?
</span>
<span>
<a class="blue" href="{{fastReg}}">快速注册</a>
</span>
</li>
<li class="third-party-login">
<a href="{{weixinLogin}}">
<span class="icon weixin"></span>
</a>
<a href="{{qqLogin}}">
<span class="icon qq"></span>
</a>
<a href="{{weiboLogin}}">
<span class="icon weibo"></span>
</a>
<a href="{{alipayLogin}}">
<span class="icon alipay"></span>
</a>
<a href="{{doubanLogin}}">
<span class="icon douban"></span>
</a>
<a href="{{renrenLogin}}">
<span class="icon renren"></span>
</a>
</li>
</ul>
<input id="country-code-hide" name="countryCode" type="hidden" value="{{countryCode}}">
</div>
</li>
</ul>
<input id="country-code-hide" name="countryCode" type="hidden" value="{{countryCode}}">
{{/ passport}}
</div>
... ...
{{> sign-header}}
<div class="passport-page yoho-page clearfix">
{{# passport}}
{{> reg/cover}}
<div class="content">
<div class="register-page">
<div class="success-box">
<div class="success-text">
<span>恭喜!</span>账号注册成功,&nbsp;<span id="count-down">5</span>&nbsp;&nbsp;秒后将跳转至首页
</div>
<a class="success-btn" href="{{goShoppong}}" data-url="{{goUrl}}">随便逛逛</a>
<div class="success-box">
<div class="success-text">
恭喜您,账号注册成功!
</div>
<div>
尊敬的{{mobile}},恭喜您已经成为YOHO!BLK会员!即刻您可以开启时尚购物之旅!
</div>
<a class="success-btn" href="{{goShoppong}}" data-url="{{goUrl}}">开始购物</a>
</div>
</div>
{{/ passport}}
</div>
... ...
<div class="passport-cover">
<div class="cover-content">
{{#if coverHref}}
<a href="{{coverHref}}" target="_bank">
<img class="cover-img" src="{{coverImg}}">
</a>
{{^}}
<img class="cover-img" src="{{coverImg}}">
{{/if}}
</div>
</div>
<div class="passport-cover">
<div class="cover-content">
{{#if coverHref}}
<a href="{{coverHref}}" target="_bank">
<img class="cover-img" src="{{coverImg}}">
</a>
{{^}}
<img class="cover-img" src="{{coverImg}}">
{{/if}}
</div>
</div>
\ No newline at end of file
<div class="register-page">
<ul>
<li class="clearfix">
<select id="region" class="region" name="region">
{{#each region}}
<option {{#if selected}}selected="selected"{{/if}} value="{{areaCode}}">{{name}}</option>
{{/each}}
</select>
</li>
<li class="clearfix" data-index="0">
<span id="country-code" class="country-code">{{location}}</span>
<input value="" id="phone-num" class="input va phone-num" type="text" name="phoneNum" placeholder="请输入手机号码" autocomplete="off">
</li>
<li class="w330 clearfix" data-index="1">
<input id="captcha" class="input va captcha" type="text" name="captcha" placeholder="图形验证码" autocomplete="off" maxlength="4">
<img id="captcha-img" class="captcha-img" src="{{captchaUrl}}" alt="">
<a class="link change-captcha">换一张</a>
</li>
<li class="clearfix" data-index="2">
<input id="msg-captcha" class="input va msg-captcha" type="text" name="msgCaptcha" placeholder="短信验证码" autocomplete="off" maxlength="4">
<input id="send-captcha" class="btn send-captcha disable" type="button" value="获取短信验证码">
<span id="msg-tip" class="hide msg-tip">短信验证码已发送至您的手机,请查收</span>
</li>
<li class="clearfix" data-index="3">
<input id="pwd" class="input va pwd" name="pwd" placeholder="设置密码" autocomplete="off" maxlength="20" type="password">
<div class="pwd-intensity-container">
<span class="pwd-intensity low"></span>
<span class="pwd-intensity mid"></span>
<span class="pwd-intensity high"></span>
</div>
<div id="pwd-tips" class="hide pwd-tips">
<div class="default" id="pwd-tip1"><i></i>密码只支持6-20位字符</div>
<div class="default" id="pwd-tip2"><i></i>由字母、 数字组合,不能包含特殊符号</div>
</div>
</li>
<li class="items-container clearfix">
<input id="agree-terms" class="agree-terms" type="checkbox" checked="">
<span>
我已阅读并同意遵守
<a class="link go-yoho-items" href="{{itemUrl}}" target="_blank">YOHO!BUY 有货服务条款</a>
</span>
</li>
<li class="clearfix">
<input name="refer" id="refer" type="hidden" value="{{referUrl}}">
<input id="register-btn" class="btn register-btn disable" type="submit" value="{{regBtnText}}" disabled="">
</li>
{{# loginUrl}}
<li class="quick-login-container">
我已注册YOHO!BUY 有货账号
<a class="link go-login" href="{{.}}">快速登录</a>
</li>
{{/loginUrl}}
{{# skipUrl}}
<li class="skip-user-info">
<a href="{{.}}">跳过此步</a>
</li>
{{/skipUrl}}
</ul>
<div id="err-tip" class="err-tip hide">
<span></span>
<b></b>
</div>
<input name="" type="hidden" id="open-id" value="{{openId}}"/>
<input name="" type="hidden" id="source-type" value="{{sourceType}}"/>
</div>
<div class="passport-cover">
<div class="cover-content">
{{#if coverHref}}
<a href="{{coverHref}}" target="_bank">
<img class="cover-img" src="{{coverImg}}">
</a>
{{^}}
<img class="cover-img" src="{{coverImg}}">
{{/if}}
</div>
</div>
\ No newline at end of file
<div class="register-page">
<div class="register-page center-content">
<ul>
<li class="w330 clearfix">
<div class="title">
JOIN YOHO!BLK
</div>
</li>
<li class="w330 clearfix" data-index="0">
<li class="clearfix relative" data-index="0">
<select id="region" class="region" name="region">
{{#each region}}
<option {{#if selected}}selected="selected"{{/if}} value="{{areaCode}}">{{name}}</option>
{{/each}}
</select>
<div id="phone" class="phone" >
<div id="phone" class="left phone relative">
<span id="country-code" class="country-code">{{location}}</span>
<input value="" id="phone-num" class="input va phone-num" type="text" name="phoneNum"
placeholder="Phone Number" autocomplete="off">
</div>
</li>
<li class="w330 clearfix" data-index="1">
<li class="clearfix" data-index="1">
<input id="captcha" class="input va captcha" type="text" name="captcha" placeholder="图形验证码"
autocomplete="off" maxlength="4">
<div>
<div class="left captcha-component">
<img id="captcha-img" class="captcha-img" src="{{captchaUrl}}" alt="">
<a class="link change-captcha"><img src="" alt=""></a>
<a class="left link change-captcha"><span class="iconfont gray">&#xe613;</span></a>
</div>
</li>
<li class="clearfix" data-index="2">
<input id="msg-captcha" class="input va msg-captcha" type="text" name="msgCaptcha" placeholder="短信验证码"
autocomplete="off" maxlength="4">
<input id="send-captcha" class="btn send-captcha disable" type="button" value="获取短信验证码">
<span id="msg-tip" class="hide msg-tip">短信验证码已发送至您的手机,请查收</span>
</li>
<li class="clearfix" data-index="3">
<div class="pwd-intensity-container">
<span class="pwd-intensity low"></span>
<span class="pwd-intensity mid"></span>
<span class="pwd-intensity high"></span>
</div>
</li>
<li class="clearfix">
<input id="pwd" class="input va pwd" name="pwd" placeholder="Password" autocomplete="off" maxlength="20"
type="password">
... ... @@ -47,35 +55,39 @@
</div>
</li>
<li class="items-container clearfix">
<input id="agree-terms" class="agree-terms" type="checkbox" checked="">
<span>
<li class="clearfix">
<span class="agree-terms checked">
<span class="iconfont checkbox small">&#xe602;</span>
我已阅读并同意遵守
<a class="link go-yoho-items" href="{{itemUrl}}" target="_blank">YOHO!BUY 有货服务条款</a>
</span>
<span class="right">
<a class="blue go-yoho-items" href="{{itemUrl}}" target="_blank">YOHO!有货服务条款</a>
</span>
</li>
<li class="clearfix">
<input name="refer" id="refer" type="hidden" value="{{referUrl}}">
<input id="register-btn" class="btn register-btn disable" type="submit" value="{{regBtnText}}" disabled="">
<div style="width: 100%;text-align: center">
<input id="register-btn" class="btn register-btn disable" type="submit" value="{{regBtnText}}"
disabled="">
</div>
</li>
{{# loginUrl}}
<li class="quick-login-container">
<li class="clearfix quick-login-container">
<div style="width: 100%;text-align: center">
已注册YOHO!BLK账号
<a class="link go-login" href="{{.}}">快速登录</a>
</li>
{{/loginUrl}}
{{# skipUrl}}
<li class="skip-user-info">
<a href="{{.}}">跳过此步</a>
</li>
{{/skipUrl}}
<a class="blue go-login" href="">快速登录</a>
</div>
</li>
</ul>
<div id="err-tip" class="err-tip hide">
<span></span>
<b></b>
</div>
<div class="tips hide">
<div class="triangle"></div>
<div class="rectangle"></div>
</div>
<input name="" type="hidden" id="open-id" value="{{openId}}"/>
<input name="" type="hidden" id="source-type" value="{{sourceType}}"/>
</div>
... ...
<div id="select" class="select relative">
<div id="header" class="header">
<div class="name"> 中国</div>
<span class="iconfont">&#xe617;</span>
</div>
<div id="phone" class="phone">
<span id="country-code" class="country-code">{{countryCode}}</span>
<input id="account" class="account input phone-num va" name="account" value="{{bindMobile}}"
type="text"
placeholder="Phone Number" autocomplete="off">
</div>
</div>
<div>
<div id="options" class="options">
<div name="option" class="option" value="+1">
<span name="status" class="selected iconfont">&#xe60b;</span>
<div name="name" class="name">中国</div>
</div>
</div>
<div name="option" class="option touch" value="+1">
<span name="status" class="selected iconfont">&#xe60b;</span>
<div name="name" class="name">中国澳门</div>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -17,6 +17,7 @@ module.exports = {
subDomains: {
default: '//www.yohoblk.com'
},
cookieDomain: 'yohoblk.com',
domains: {
api: 'http://api.yoho.yohoops.org/', // devapi.yoho.cn:58078 testapi.yoho.cn:28078 devapi.yoho.cn:58078
service: 'http://devservice.yoho.cn:28077/', // testservice.yoho.cn:28077 devservice.yoho.cn:58077
... ...
... ... @@ -10,8 +10,7 @@ module.exports = app => {
app.use('/partial', require('./apps/partial')); // 组件demo
// 业务模块
// app.use('/passport', require('./apps/passport'));
app.use('/product', require('./apps/product'));
app.use('/passport', require('./apps/passport'));
app.use('/', require('./apps/channel')); // 频道页
app.use('/me', require('./apps/me')); // 个人中心
... ...
... ... @@ -33,6 +33,7 @@
"dependencies": {
"bluebird": "^3.4.0",
"body-parser": "^1.15.0",
"captchapng": "0.0.1",
"connect-memcached": "^0.2.0",
"cookie-parser": "^1.4.3",
"express": "^4.13.1",
... ... @@ -46,6 +47,12 @@
"morgan": "^1.7.0",
"oneapm": "^1.2.20",
"passport": "^0.3.2",
"passport-douban": "0.0.1",
"passport-local": "^1.0.0",
"passport-qq": "0.0.3",
"passport-renren": "^0.1.3",
"passport-sina": "^0.1.0",
"passport-strategy": "1.x.x",
"passport-weixin": "^0.1.0",
"request-promise": "^3.0.0",
"serve-favicon": "^2.3.0",
... ...
... ... @@ -7,15 +7,17 @@ var $ = require('yoho-jquery');
var $account = $('#account'),
$password = $('#password'),
$captcha = $('#captcha');
$captcha = $('#captcha'),
$login = $('#login-btn'),
$phone = $('#phone');
var $accountTip = $account.siblings('.err-tip'),
var $accountTip = $phone.siblings('.err-tip'),
$passwordTip = $password.siblings('.err-tip'),
$captchaTip = $captcha.siblings('.err-tip'),
$loginTip = $login.siblings('.login-fail-tip'),
$capsLock = $('#caps-lock');
var $countryCodeHide = $('#country-code-hide'),
$countryCodeEm = $('#country-code > em'),
var $countryCodeEm = $('#country-code'),
$countryList = $('#country-list');
var $emailAutoComplete = $('#email-autocomplete');
... ... @@ -32,26 +34,27 @@ var $captchaWrap = $('.captcha-wrap'),
// checkbox status unicode
var checkbox = {
checked: '&#xe612;',
unchecked: '&#xe613;'
checked: '&#xe602;',
unchecked: '&#xe601;'
};
var authing = false;
var emailAcTime;
var ERR_ACCOUNT_EMPTY = '请输入账号';
$captcha = $captchaWrap.find('#captcha');
$captchaTip = $captchaWrap.find('.err-tip');
require('../../plugins/tips');
require('yoho-jquery-placeholder');
// 验证账户名
function validateAccount() {
var pass = false,
account = $.trim($account.val()),
countryCode = $countryCodeHide.val(),
countryCode = $countryCodeEm.val(),
err;
if (account !== '') {
... ... @@ -73,20 +76,52 @@ function validateAccount() {
err = '邮箱格式不正确,请重新输入';
}
}
} else {
err = '请输入账户名';
err = ERR_ACCOUNT_EMPTY;
}
if (pass) {
$accountTip.addClass('hide');
$account.removeClass('error');
} else {
$accountTip.removeClass('hide').children('em').text(err);
$account.addClass('error');
}
return pass;
}
function disableTips() {
'use strict';
$phone.tips('hide');
}
function enableTips() {
'use strict';
$phone.tips('show', '× 账号不存在');
}
function tryUser() {
return $.ajax({
type: 'POST',
url: '/passport/login/user',
data: {
phoneNum: $account.val(),
area: $countryCodeEm.val().replace('+', '')
}
}).then(function(data) {
if (data.code && data.code === 200) {
authing = false;
disableTips();
} else {
enableTips();
authing = true;
}
});
}
// 验证密码
function validatePassword() {
var pass = false,
... ... @@ -105,10 +140,8 @@ function validatePassword() {
if (pass) {
$passwordTip.addClass('hide');
$password.removeClass('error');
} else {
$passwordTip.removeClass('hide').children('em').text(err);
$password.addClass('error');
}
return pass;
}
... ... @@ -136,10 +169,8 @@ function validateCaptcha() {
if (pass) {
$captchaTip.addClass('hide');
$captcha.removeClass('error');
} else {
$captchaTip.removeClass('hide').children('em').text(err);
$captcha.addClass('error');
}
return pass;
}
... ... @@ -154,16 +185,14 @@ function validate() {
pass = validateAccount() && validatePassword() && validateCaptcha();
} else {
pass = false;
$account.addClass('error');
if (password === '') {
// 账户名和密码都为空的情况下点击登陆,只在账户输入框后显示错误提示
$accountTip.addClass('both-error').removeClass('hide').children('em').text('请输入账户名和密码');
$passwordTip.addClass('hide');
$password.addClass('error');
} else {
$accountTip.removeClass('hide').children('em').text('请输入账户名');
$accountTip.removeClass('hide').children('em').text(ERR_ACCOUNT_EMPTY);
}
}
... ... @@ -188,7 +217,7 @@ function login() {
url: '/passport/login/auth',
type: 'POST',
data: {
areaCode: $countryCodeHide.val().replace('+', ''),
areaCode: $countryCodeEm.val().replace('+', ''),
account: $.trim($account.val()),
password: $.trim($password.val()),
captcha: $.trim($captcha.val()),
... ... @@ -204,10 +233,10 @@ function login() {
} else {
if (res.data.errorType === 'captcha') {
$captchaTip.removeClass('hide').children('em').html(res.message);
$captcha.addClass('error').val('');
$captcha.val('');
} else {
$passwordTip.removeClass('hide').children('em').html(res.message);
$password.addClass('error').val('');
$loginTip.removeClass('hide').children('em').html(res.message);
$password.val('');
}
// 验证错误次数
... ... @@ -224,58 +253,88 @@ function login() {
}
mailAc($account, function() {
if (validateAccount()) {
$.ajax({
url: '/passport/login/account',
type: 'GET',
data: {
account: $.trim($account.val())
}
tryUser().then(function() {
'use strict';
return $.ajax({
url: '/passport/login/account',
type: 'GET',
data: {
account: $.trim($account.val())
}
});
}).then(function(res) {
'use strict';
if (res.data && res.data.needCaptcha) {
vaAccountErrTimes();
}
});
}
}
);
});
$('[placeholder]').placeholder();
$account.on('keyup', function() {
'use strict';
var value = $.trim($(this).val());
// 展开地区列表
$('#country-code').on('click', function() {
if ($countryList.css('display') === 'none') {
$countryList.slideDown();
if (value.length === 0) {
disableTips();
}
}).on('focus', function() {
'use strict';
$phone.addClass('focus-gain');
}).on('blur', function() {
'use strict';
$phone.removeClass('focus-gain');
});
// 选中地区列表项
$countryList.on('click', 'li', function() {
var $this = $(this),
cc = $this.data('cc');
$('[placeholder]').placeholder();
$countryCodeEm.html($this.html());
// 展开地区列表
// $('#country-code').on('click', function() {
// if ($countryList.css('display') === 'none') {
// $countryList.slideDown();
// }
// });
$countryCodeHide.val(cc);
// 选中地区列表项
// $countryList.on('click', 'li', function() {
// var $this = $(this),
// cc = $this.data('cc');
//
// $countryCodeEm.html($this.html());
//
// $countryCodeHide.val(cc);
//
// //$countryList.slideUp();
// });
$countryList.change(function() {
'use strict';
var $this = $(this);
$countryCodeEm.text($this.val());
$countryList.slideUp();
});
// 点击其他区域,收起区域列表
$(document).on('click', function(e) {
if ($(e.target).closest('#country-code').length > 0) {
return;
}
// $(document).on('click', function(e) {
// if ($(e.target).closest('#country-code').length > 0) {
// return;
// }
if ($countryList.css('display') === 'block') {
$countryList.slideUp();
}
});
// if ($countryList.css('display') === 'block') {
// $countryList.slideUp();
// }
// });
// 密码
$password.on('blur', function() {
validatePassword();
$password.removeClass('focus-gain');
validatePassword();
if ($capsLock.hasClass('hide')) {
return;
}
... ... @@ -290,11 +349,19 @@ $password.on('blur', function() {
return;
}
$capsLock.addClass('hide');
}).on('focus', function() {
'use strict';
$password.addClass('focus-gain');
});
// 验证码
$captcha.on('blur', function() {
$captcha.removeClass('focus-gain');
validateCaptcha();
}).on('focus', function() {
'use strict';
$captcha.addClass('focus-gain');
});
// 邮箱自动完成列表项点击
... ... @@ -313,9 +380,9 @@ $remember.on('click', function() {
$this.toggleClass('checked');
if ($this.hasClass('checked')) {
$this.children('i').html(checkbox.checked);
$this.children('span').html(checkbox.checked);
} else {
$this.children('i').html(checkbox.unchecked);
$this.children('span').html(checkbox.unchecked);
}
});
... ... @@ -323,8 +390,6 @@ $remember.on('click', function() {
$('.va').on('focus', function() {
var $this = $(this);
$this.removeClass('error');
$this.siblings('.err-tip').addClass('hide');
});
... ... @@ -334,7 +399,7 @@ $captchaWrap.on('click', '.change-captcha, .captcha-img', function() {
});
// 登录
$('#login-btn').on('click', login);
$login.on('click', login);
// Enter登录
$('input.va').on('keypress', function(e) {
... ...
... ... @@ -10,12 +10,13 @@ var $ = require('yoho-jquery'),
var $registerPage = $('.register-page'),
$pwdTips = $('#pwd-tips'),
$pwdTip1 = $pwdTips.find('#pwd-tip1'),
$errTip = $('#err-tip'),
$registerBtn = $('#register-btn'),
$countDown = $('#count-down'),
$successBtn = $('.success-btn'),
countDown = 5,
clearT;
$errTip = $('.tips'),
$registerBtn = $('#register-btn');
// $countDown = $('#count-down'),
// $successBtn = $('.success-btn'),
// countDown = 5,
// clearT;
var $sendCaptcha = $('#send-captcha'),
caCount = 4,
... ... @@ -24,7 +25,6 @@ var $sendCaptcha = $('#send-captcha'),
var $pn = $('#phone-num'),
$mc = $('#msg-captcha'),
$pwd = $('#pwd'),
$repwd = $('#repwd'),
$ca = $('#captcha'),
timeResidue = 0;
... ... @@ -36,8 +36,7 @@ var $pwdIntensity = $('.pwd-intensity'),
var $region = $('#country-code'),
$regionSelect = $('#region');
var isPwd = false,
pwdVal;
var $agreeTerms = $('.agree-terms');
var pwdReg = regValidate.pwdValidateRegx;
... ... @@ -45,13 +44,18 @@ var $curErrContainer;
var oldPhone = ''; // 如果手机号改变时刷新验证码
// checkbox status unicode
var checkbox = {
checked: '&#xe602;',
unchecked: '&#xe601;'
};
require('yoho-jquery-placeholder');
setTimeout(function() {
$pn.val('');
$mc.val('');
$pwd.val('');
$repwd.val('');
$ca.val('');
}, 0);
... ... @@ -61,11 +65,42 @@ $ca.attr('maxlength', caCount);
// 密码规则提示
$pwd.focus(function() {
$pwdTips.removeClass('hide');
isPwd = true;
pwdVal = $(this).val();
// isPwd = true;
// pwdVal = $(this).val();
}).blur(function() {
$pwdTips.addClass('hide');
isPwd = false;
// isPwd = false;
});
// 同意服务条款
$agreeTerms.on('click', function() {
var $this = $(this);
var i,
passI = 0;
$this.toggleClass('checked');
if ($this.hasClass('checked')) {
$this.children('span').html(checkbox.checked);
// 勾选
for (i = 0; i < validateResult.length; i++) {
if (validateResult[i].status) {
passI++;
}
}
if (passI === 4) {
$registerBtn.removeClass('disable').removeAttr('disabled');
}
} else {
$this.children('span').html(checkbox.unchecked);
// 取消勾选
$registerBtn.addClass('disable').attr('disabled', 'disabled');
}
});
... ... @@ -75,7 +110,7 @@ $('[placeholder]').placeholder();
// 存储校验信息
validateResult = [
{
id: 'phone-num',
id: 'phone',
message: '', // 错误信息
status: false // 当前的状态
},
... ... @@ -93,11 +128,6 @@ validateResult = [
id: 'pwd',
message: '',
status: false
},
{
id: 'repwd',
message: '',
status: false
}
];
... ... @@ -356,23 +386,7 @@ function validateRule(page, $element, callback) {
}
return callback();
// 二次密码校验
} else if ($element.hasClass('repwd')) {
if (val === '') {
validateResult[4].message = '请输入密码确认';
validateResult[4].status = false;
} else if ($pwd.val() !== val) {
validateResult[4].message = '与密码不一致,请重新输入';
validateResult[4].status = false;
} else {
validateResult[4].message = '';
validateResult[4].status = true;
}
return callback();
}
}
... ... @@ -380,8 +394,10 @@ function posErrTip() {
var tipPos = $curErrContainer.offset();
return $errTip.css({
top: tipPos.top - 40,
left: tipPos.left
top: tipPos.top + $curErrContainer.height() - 2,
left: tipPos.left,
width: $curErrContainer.width() + 2,
height: $curErrContainer.height()
});
}
... ... @@ -399,7 +415,7 @@ function showErrTip() {
if (!!validateResult[i].message && validateResult[i].message !== 'err') {
// 显示错误提示
$errTip.find('span').text(validateResult[i].message);
$errTip.find('.rectangle').text(validateResult[i].message);
$curErrContainer = $('#' + validateResult[i].id);
posErrTip().removeClass('hide');
... ... @@ -411,29 +427,6 @@ function showErrTip() {
}
}
// 显示红色边框
function showBorder() {
var $errInput,
i,
validateResultLen = validateResult.length;
for (i = 0; i < validateResultLen; i++) {
if (validateResult[i].message) {
// 显示红色边框
$errInput = $('#' + validateResult[i].id);
$errInput.addClass('error');
} else {
// 去掉红色边框
$errInput = $('#' + validateResult[i].id);
$errInput.removeClass('error');
}
}
}
// 密码强度验证
function pwdFn($obj) {
var pwd = $obj.val(),
... ... @@ -497,32 +490,32 @@ $(window).resize(function() {
// ( ▼-▼ )注册页和信息完善页面接口不同
exports.init = function(page) {
$('#agree-terms').click(function() {
var $this = $(this),
i,
passI = 0;
if ($this.attr('notchecked')) {
$this.removeAttr('notchecked');
// 勾选
for (i = 0; i < validateResult.length; i++) {
if (validateResult[i].status) {
passI++;
}
}
if (passI === 4) {
$registerBtn.removeClass('disable').removeAttr('disabled');
}
} else {
// 取消勾选
$registerBtn.addClass('disable').attr('disabled', 'disabled');
$this.attr('notchecked', 'notchecked');
}
});
// $('#agree-terms').click(function() {
//
// var $this = $(this),
// i,
// passI = 0;
//
// if ($this.('notchecked') {
//
// $this.removeAttr('notchecked');
//
// // 勾选
// for (i = 0; i < validateResult.length; i++) {
// if (validateResult[i].status) {
// passI++;
// }
// }
// if (passI === 4) {
// $registerBtn.removeClass('disable').removeAttr('disabled');
// }
// } else {
//
// // 取消勾选
// $registerBtn.addClass('disable').attr('disabled', 'disabled');
// $this.attr('notchecked', 'notchecked');
// }
// });
// 按回车键提交
$registerPage.on('keydown', function(e) {
... ... @@ -542,7 +535,6 @@ exports.init = function(page) {
validateRule(page, $(this), function() {
showErrTip(); // 显示错误提示
showBorder(); // 显示红色边框
// 如果validateResult中有4个status为true表示验证通过
for (j = 0; j < vLen; j++) {
... ... @@ -605,7 +597,7 @@ exports.init = function(page) {
$('#msg-tip').removeClass('hide');
$sendCaptcha.addClass('disable').attr('disabled', 'disabled');
$sendCaptcha.val('60秒可重新发送');
$sendCaptcha.val('60秒可重新操作');
t = setInterval(function() {
if (timeResidue <= 0) {
... ... @@ -613,7 +605,7 @@ exports.init = function(page) {
clearInterval(t);
return;
}
$sendCaptcha.val(timeResidue-- + '秒可重新发送');
$sendCaptcha.val(timeResidue-- + '秒可重新操作');
}, 1000);
if (page === 'reg') {
... ... @@ -710,16 +702,16 @@ exports.init = function(page) {
});
// 注册成功页面5秒后跳转
if ($('.success-box').length > 0) {
clearT = setInterval(function() {
if (countDown === 0) {
window.location.href = $successBtn.attr('data-url');
clearInterval(clearT);
}
$countDown.text(countDown--);
}, 1000);
}
// if ($('.success-box').length > 0) {
// clearT = setInterval(function() {
//
// if (countDown === 0) {
//
// window.location.href = $successBtn.attr('data-url');
//
// clearInterval(clearT);
// }
// $countDown.text(countDown--);
// }, 1000);
// }
};
... ...
... ... @@ -2,10 +2,10 @@
* Created by TaoHuang on 2016/7/1.
*/
var $ = require('yoho-jquery'),
var jQuery = require('yoho-jquery'),
Handlebars = require('yoho-handlebars');
(function ($) {
((function($) {
var tpl = '<div class="tips">' +
'<div class="triangle"></div>' +
... ... @@ -14,23 +14,35 @@ var $ = require('yoho-jquery'),
var tplFn = Handlebars.compile(tpl);
var isInit = false;
var _that = this;
var $tips = null;
var methods = {
show: function () {
if (isInit) {
show: function() {
if (_that.isInit) {
$tips = this.find('.tips');
this.find('.rectangle').text(arguments[0]);
} else {
_isInit = true;
this.append(tplFn({content:arguments[0]}));
_that.isInit = true;
this.append(tplFn({content: arguments[0]}));
$tips = this.find('.tips');
$tips.css('top', this.height() - 2);
}
},
hide: function () {
this.find('.tips').remove();
hide: function() {
if (_that.isInit) {
this.find('.tips').remove();
_that.isInit = false;
}
},
status: function() {
return _that.isInit;
}
};
$.fn.tips = function (method) {
_that.isInit = false;
$.fn.tips = function(method) {
// 方法调用
if (methods[method]) {
... ... @@ -41,4 +53,4 @@ var $ = require('yoho-jquery'),
};
})($);
\ No newline at end of file
})(jQuery));
... ...
... ... @@ -12,6 +12,7 @@
@import "components/index";
@import "product/index";
@import "channel/index";
@import "passport/index";
/* 资讯 */
@import "editorial/index";
... ...
$theme-color: #1d1d1d;
$input-button: 125px;
$item-height: 43px;
$item-width: 340px;
$bord-color: #dbdbdb;
$margin-left: 15px;
$margin-top: 130px;
$red: #e01;
$captcha: 195px;
$captcha-img-bg-color: #eee;
$err-tips-width: 348px;
$captcha-fresh: #999;
$blue: #379ed6;
$inactive-color: #555;
$option-color:#f8f8f8;
@define-mixin li-setting {
margin-bottom: 20px;
width: 100%;
height: $item-height;
position: relative;
}
@define-mixin passport-page {
font-size: 14px;
width: $item-width;
margin: $margin-top auto;
}
@define-mixin region {
height: $item-height;
padding: 4px 0;
width: 100px;
border-color: #c1c1c1;
color: #9a9a9a;
font-size: 16px;
font-family: '黑体' Regular;
float: left;
text-indent: 10px;
}
@define-mixin country-code {
width: 50px;
height: $item-height;
line-height: $item-height;
text-align: center;
white-space: nowrap;
font-size: 16px;
float: left;
}
@define-mixin phone {
width: 225px;
margin-left: $margin-left;
border: 1px solid $bord-color;
display: inline-block;
}
@define-mixin input {
height: $item-height;
line-height: 43px;
font-size: 16px;
width: 100%;
border: 1px solid $bord-color;
text-indent: 10px;
color: #9a9a9a;
padding: 0;
}
@define-mixin small {
font-size: 1em;
}
... ...
@import "config";
.passport-page {
width: 1150px;
margin-left: auto;
... ... @@ -13,12 +15,18 @@
height: 43px;
font-size: 16px;
line-height: 1.2;
line-height: 43px\9;
line-height: 43px;
width: 268px;
border: 1px solid #dbdbdb;
text-indent: 10px;
color: #9a9a9a;
color: rgba(0, 0, 0, .5);
color: rgba(0, 0, 0, 0.5);
}
.title {
font-size: 30px;
text-align: center;
color: $theme-color;
}
.error {
... ... @@ -32,7 +40,7 @@
width: 100%;
text-align: center;
color: #fff;
background-color: #ff1901;
background-color: $theme-color;
letter-spacing: 10px;
font-size: 20px;
cursor: pointer;
... ... @@ -50,11 +58,11 @@
&.red {
.pwd-intensity {
color: #ee0011;
color: $red;
}
.color {
background: #ee0011;
background: $red;
color: #fff;
}
}
... ... @@ -85,7 +93,7 @@
height: 15px;
font-size: 12px;
background-color: #e8e8e8;
padding: 0px 10px;
padding: 0 10px;
text-align: center;
color: #b9b9b9;
}
... ... @@ -94,12 +102,13 @@
.pwd-tips {
position: absolute;
top: -10px;
left: 285px;
width: 160px !important;
height: 72px;
left: 360px;
width: 160px;
height: 78px;
padding-top: 7px;
font-size: 12px;
background: url(/passport/tip/block.png) no-repeat;
z-index: 3;
> div {
position: relative;
... ... @@ -136,20 +145,22 @@
.email-autocomplete {
position: absolute;
width: 248px;
width: 225px;
top: calc($item-height + 3px);
padding: 0 10px;
z-index: 1;
margin-top: 5px;
border-radius: 5px;
cursor: pointer;
border: 1px solid #fefefe;
background-color: #161616;
color: #fff;
font-size: 16px;
background-color: $theme-color;
color: white;
font-size: 18px;
li {
height: 24px;
line-height: 24px;
padding-top: 10px;
height: 10px;
line-height: @height;
}
}
... ... @@ -158,26 +169,21 @@
font-size: 14px;
white-space: nowrap;
top: 8px;
left: 285px;
left: $err-tips-width;
padding: 6px 0;
color: #f00;
i {
display: block;
float: left;
height: 14px;
width: 14px;
background: url(/passport/tip/error.png) no-repeat;
margin-right: 5px;
color: $blue;
span {
font-size: 1em;
}
a {
text-decoration: underline;
color: #f00;
color: $blue;
}
}
/*完善信息提示框*/
/* 完善信息提示框 */
.page-tip {
display: block;
width: 100%;
... ... @@ -212,6 +218,7 @@
margin-top: 50px;
}
}
@import "login";
@import "register";
@import "back";
... ...
@import "config";
.login-page {
@mixin passport-page ;
li {
@mixin li-setting ;
}
.relative {
position: relative;
}
... ... @@ -7,57 +15,93 @@
float: right;
}
.login-ul > li {
margin-bottom: 25px;
}
.login-fail-tip {
position: absolute;
top: -20px;
color: $blue;
span {
font-size: 0.8em;
color: $blue;
font-weight: bold;
}
.title {
float: left;
font-size: 20px;
}
.country-code {
cursor: pointer;
color: #b9b9b9;
@mixin country-code ;
}
.country-list {
display: none;
position: absolute;
padding: 0 10px;
background: #fff;
border: 1px solid #000;
top: 20px;
right: 0;
z-index: 1;
border-radius: 5px;
@mixin region ;
}
.phone {
@mixin phone ;
&.focus-gain {
border: 2px solid #000;
}
li {
height: 20px;
line-height: 20px;
color: #000;
&.focus-lost {
border: 1px solid #dbdbdb;
}
}
.captcha-wrap {
position: relative;
.input {
@mixin input;
&.phone-num {
width: 150px;
display: block;
float: left;
border: none !important;
}
&.captcha {
width: $captcha;
float: left;
&.focus-gain {
border: 2px solid #000;
}
&.focus-lost {
border: 1px solid #dbdbdb;
}
}
.err-tip {
left: 335px;
&.password {
&.focus-gain {
border: 2px solid #000;
}
&.focus-lost {
border: 1px solid #dbdbdb;
}
}
}
/* 验证码 */
.captcha {
width: 160px;
float: left;
.login-btn {
margin-top: 40px;
}
.captcha-component {
margin-left: $margin-left;
background-color: $captcha-img-bg-color;
width: 130px;
}
.gray {
color: $captcha-fresh;
}
/* 验证码 */
.captcha-img {
height: 37px;
width: 90px;
border: 0;
vertical-align: middle;
outline: none;
margin-left: 10px;
margin-top: 4px;
... ... @@ -65,24 +109,25 @@
}
.change-captcha {
position: absolute;
top: 0;
right: -50px;
cursor: pointer;
float: left;
height: 43px;
font-size: 14px;
line-height: 43px;
color: #ff1901;
line-height: @height;
}
.remember-me {
cursor: pointer;
.small {
@mixin small;
}
}
.other-opts {
color: #b9b9b9;
font-size: 14px;
text-align: center;
.iconfont {
font-size: 14px;
... ... @@ -93,10 +138,6 @@
}
}
.forget-password {
color: #b9b9b9;
}
.third-party-login {
display: table;
... ... @@ -150,4 +191,45 @@
height: 27px;
line-height: 30px;
}
/* 选择国家 */
.select {
.header {
float: left;
.name {
display: inline-block;
}
.icon {
color: red;
}
}
}
.options {
position: absolute;
background-color: #f8f8f8;
top: $item-height;
left: 0;
z-index: 100;
font-size: 1.2em;
.option {
width: $item-width;
.selected {
display: inline-block;
width: 50px;
color: red;
text-align: center;
}
.name {
display: inline-block;
}
&.touch {
background-color: #666;
}
}
}
}
... ...
$theme-color: #ffffff !default;
$inactive-color: #grey !default;
@import "config";
.register-page {
font-size: 14px;
color: #b9b9b9;
background-color: red;
width: 300px;
@mixin passport-page;
li {
margin-bottom: 25px;
width: 270px;
position: relative;
&.w330 {
width: 330px;
}
@mixin li-setting;
input,
select {
outline: none;
}
.titile {
font-size:30px;
}
.phone{
display: inline;
}
.input {
height: 43px;
line-height: 43px;
font-size: 16px;
width: 268px;
border: 1px solid #dbdbdb;
text-indent: 10px;
color: #9a9a9a;
padding: 0;
@mixin input;
&.error {
border: 1px solid red;
}
&.phone-num {
width: 209px;
width: 160px;
display: block;
float: left;
position: absolute;
top: 0;
right: 0;
border: none !important;
}
&.captcha,
&.msg-captcha {
width: 160px;
width: $captcha;
float: left;
}
... ... @@ -63,11 +36,15 @@ $inactive-color: #grey !default;
}
}
.phone {
@mixin phone;
}
.send-captcha {
top: 0;
background: $theme-color;
position: absolute;
width: 200px;
width: $input-button;
right: 0;
font-size: 12px;
text-indent: 0;
... ... @@ -78,11 +55,6 @@ $inactive-color: #grey !default;
}
}
.link {
text-decoration: underline;
color: #ff1901;
}
.btn {
height: 45px;
line-height: 45px;
... ... @@ -93,111 +65,95 @@ $inactive-color: #grey !default;
text-align: center;
}
/*区域下拉选择框*/
/* 区域下拉选择框 */
.region {
height: 30px;
padding: 4px 0;
width: 132px;
border-color: #c1c1c1;
color: #9a9a9a;
font-size: 16px;
font-family: '黑体' Regular;
@mixin region;
}
/*手机号*/
/* 手机号 */
.country-code {
width: 58px;
height: 43px;
line-height: 43px;
text-align: center;
white-space: nowrap;
border: 1px solid #dbdbdb;
border-right: 0;
font-size: 16px;
display: block;
@mixin country-code;
}
.captcha {
width: $input-button;
float: left;
}
/*验证码*/
.captcha-component {
margin-left: $margin-left;
background-color: $captcha-img-bg-color;
}
/* 验证码 */
.captcha-img {
height: 37px;
height: $item-height;
width: 90px;
border: 0;
vertical-align: middle;
outline: none;
margin-left: 10px;
margin-top: 4px;
float: left;
}
.change-captcha {
margin-left: 10px;
cursor: pointer;
float: left;
height: 43px;
width: 35px;
line-height: 43px;
}
/*密码强度*/
/* 密码强度 */
.pwd-intensity-container {
width: 270px;
text-align: right;
width: 100%;
margin-top: 5px;
height: auto;
overflow: hidden;
text-align: center;
.pwd-intensity {
height: 15px;
display: inline-block;
height: 18px;
line-height: @height;
width: 108px;
font-size: 12px;
background-color: #e8e8e8;
padding: 1px 10px;
text-align: center;
color: white !important;
}
&.red {
color: red;
.color {
background: red;
background: #999;
color: #fff;
}
}
&.yellow .color {
background: #ff0;
background: #666;
color: #fff;
}
&.green .color {
background: #3ee392;
background: #1b1b1b;
color: #fff;
}
}
/*服务条款*/
&.items-container {
font-size: 13px;
text-align: right;
white-space: nowrap;
}
/*立即注册*/
/* 立即注册 */
.register-btn {
width: 270px;
display: inline-block;
width: 180px;
font-size: 20px;
font-weight: bold;
background: #ff1901;
background: $theme-color;
&.disable {
background: #555;
}
}
/*立即登录*/
/* 立即登录 */
&.quick-login-container {
width: 270px;
width: 100%;
font-size: 13px;
text-align: right;
text-align: center;
white-space: nowrap;
}
... ... @@ -210,12 +166,19 @@ $inactive-color: #grey !default;
text-decoration: underline;
}
}
.agree-terms {
cursor: pointer;
.small {
@mixin small;
}
}
}
.msg-tip {
position: absolute;
top: 17px;
left: 285px;
left: $item-width;
white-space: nowrap;
&:before {
... ... @@ -233,7 +196,7 @@ $inactive-color: #grey !default;
position: absolute;
height: 30px;
line-height: 30px;
color: red;
color: $blue;
background-color: #ffebeb;
border: 1px solid #ffbdbe;
padding: 0 10px;
... ... @@ -249,48 +212,40 @@ $inactive-color: #grey !default;
}
}
/*注册成功*/
.success-box {
font-size: 14px;
color: #b9b9b9;
margin-top: 130px;
}
.success-text {
line-height: 29px;
padding-left: 46px;
font-size: 16px;
color: #000;
/* 注册成功 */
.success-box {
font-size: 14px;
color: #b9b9b9;
margin: $margin-top auto;
width: 800px;
span {
font-size: 22px;
}
.success-text {
line-height: 29px;
font-size: 22px;
color: $theme-color;
text-align: center;
&:before {
content: '';
display: inline-block;
background: url(/passport/tip/success-icon.png) no-repeat;
width: 29px;
height: 29px;
margin-right: 5px;
position: relative;
top: 6px;
}
}
#count-down {
color: #ff1901;
}
}
div {
font-size: 16px;
text-align: center;
color: $theme-color;
margin-top: 30px;
}
.success-btn {
display: block;
margin: 46px auto 0;
width: 270px;
height: 45px;
line-height: 45px;
background: #ff1901;
text-align: center;
color: #fff;
letter-spacing: 2px;
}
.success-btn {
display: block;
margin: 46px auto 0;
width: 180px;
height: 45px;
line-height: 45px;
background: $theme-color;
text-align: center;
color: #fff;
letter-spacing: 2px;
text-align: center;
}
}
... ...
.tips {
position:relative;
top: -2px;
z-index:6;
}
.rectangle {
width:100%;
height:35px;
background:#379ED6;
color:#fff;
padding-left:10px;
line-height:35px;
font-size: 0.9em;
font-weight:bold;
position: absolute;
}
top: 41px;
z-index: 6;
width: inherit;
.triangle {
position: absolute;
left:12px;
width:0;
height:0;
border-width:5px;
top: -10px;
border-style:solid dashed dashed dashed;
border-color: transparent transparent #379ED6 transparent;
}
\ No newline at end of file
.rectangle {
width: 100%;
height: 35px;
background: #379ed6;
color: #fff;
padding-left: 10px;
line-height: 35px;
font-size: 0.9em;
font-weight: bold;
position: absolute;
}
.triangle {
position: absolute;
left: 12px;
width: 0;
height: 0;
border-width: 5px;
top: -10px;
border-style: solid dashed dashed;
border-color: transparent transparent #379ed6;
}
}
... ...