Authored by 毕凯

Merge branch 'master' into release/5.7.1

'use strict';
const questionModel = require('../models/question');
const headerModel = require('../../../doraemon/models/header'); // 头部model
exports.list = (req, res, next) => {
req.ctx(questionModel).getQuestionList().then(result => {
res.render('question/list', {
title: '调研中心',
module: '3party',
page: 'question-list',
pageHeader: headerModel.setNav({
navTitle: '调研中心'
}),
list: result,
isApp: req.yoho.isApp,
localCss: true
});
}).catch(next);
};
exports.check = (req, res, next) => {
let params = req.body;
params.uid = req.user.uid || params.uid;
if (!params.uid) {
return res.send({code: 400, message: '请先登录!'});
}
req.ctx(questionModel).getQuestionStatus(params).then(result => {
res.send(result);
}).catch(next);
};
exports.submit = (req, res, next) => {
let params = req.body;
params.uid = req.user.uid || params.uid;
if (!params.uid) {
return res.send({code: 400, message: '请先登录!'});
}
// 标识问卷来源
if (req.yoho.isApp) {
params.sourceType = 'APP';
} else if (req.yoho.mobile) {
params.sourceType = 'H5';
} else {
params.sourceType = 'PC';
}
req.ctx(questionModel).submitQuestion(params).then(result => {
res.send(result);
}).catch(next);
};
exports.detail = (req, res, next) => {
let id = parseInt(`0${req.params.id}`, 10);
req.ctx(questionModel).getQuestionDetail(id, req.user.uid).then(result => {
if (result && result.detail && req.yoho.isApp) {
result.detail.uid = req.user.uid;
}
res.render('question/detail', Object.assign(result, {
title: 'YOHOBUY!潮流大调查',
module: '3party',
page: 'question-detail',
pageHeader: headerModel.setNav({
navTitle: 'YOHOBUY!潮流大调查'
}),
isApp: req.yoho.isApp,
localCss: true
}));
}).catch(next);
};
... ...
/**
* question model
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2017/05/23
*/
const _ = require('lodash');
module.exports = class extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
}
getQuestionList() {
return this.get({
url: 'activity/question/questionList',
api: global.yoho.ServiceAPI
}).then(result => {
let list = _.get(result, 'data.rows', []);
_.forEach(list, (value, key) => {
value.index = key + 1;
if (!_.get(value, 'share.imgUrl', '')) {
_.set(value, 'share.imgUrl', 'http://img11.static.yhbimg.com/sns/2017/05/25/14/0177e28a98f73417ae1a2a146aa2670dd2.png'); // eslint-disable-line
}
});
return list;
});
}
getQuestionStatus(params) {
return this.get({
url: '/activity/question/questionValidate',
data: params,
api: global.yoho.ServiceAPI
});
}
getQuestionDetail(id, uid) {
return Promise.all([
this.get({
url: '/activity/question/questionValidate',
data: {id: id, uid: uid},
api: global.yoho.ServiceAPI
}),
this.get({
url: 'activity/question/questionDetail',
data: {id: id},
api: global.yoho.ServiceAPI
})
]).then(result => {
let resData = {};
if (_.get(result, '[0].code', '') === 200) {
let data = _.get(result, '[1].data', {});
if (data.questions) {
_.forEach(data.questions, value => {
if (+value.questionType === 3) {
value.questionContents = _.fill(Array(value.fillBlankNum || 1), true);
}
});
}
if (!_.isEmpty(data)) {
resData.detail = data;
}
} else {
resData.errText = _.get(result, '[0].message', '哟,本次调研已经飞走了,请移步其他调研呗!');
}
return resData;
});
}
submitQuestion(info) {
return this.post({
url: '/activity/question/submitQuestions',
data: info,
api: global.yoho.ServiceAPI
});
}
};
... ...
... ... @@ -10,7 +10,9 @@ const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const ads = require(`${cRoot}/ads`);
const check = require(`${cRoot}/check`);
const question = require(`${cRoot}/question`);
const validateCode = require('../passport/controllers/validateCode');
const auth = require('../../doraemon/middleware/auth');
// routers
... ... @@ -18,4 +20,10 @@ router.get('/ads', ads.index);
router.get('/check', validateCode.load, check.index);
router.post('/check/submit', validateCode.check, check.submit);
router.get('/questionnaire', auth, question.list);
router.post('/questionnaire/check', question.check);
router.post('/questionnaire/submit', question.submit);
router.get('/questionnaire/:id', auth, question.detail);
module.exports = router;
... ...
<div class="qs-detail-page">
{{# detail}}
<div class="error-tip"></div>
<div class="detail-wrap">
<p class="sub-title">{{title}}</p>
<p class="guide-tip">{{description}}</p>
<div id="qs-wrap" class="qs-wrap" data-id="{{id}}" data-cid="{{uid}}" data-title="{{share.title}}" data-desc="{{share.subtitle}}" data-img="{{share.imgUrl}}">
{{# questions}}
<dl class="qs-item" data-index="{{questionIndex}}" data-type="{{questionType}}">
<dt>{{questionTitle}}</dt>
{{#isEqualOr questionType 1}}
{{# questionContents}}
<dd class="radio-option" data-index="{{@index}}">
<span class="iconfont">&#xe6ea;</span>
<div class="option-box">{{{option}}}</div>
{{#if addon}}
<input type="text">
{{/if}}
</dd>
{{/ questionContents}}
{{/isEqualOr}}
{{#isEqualOr questionType 2}}
{{# questionContents}}
<dd class="check-option" data-index="{{@index}}">
<span class="iconfont">&#xe6ea;</span>
<div class="option-box">{{{option}}}</div>
{{#if addon}}
<input type="text">
{{/if}}
</dd>
{{/ questionContents}}
{{/isEqualOr}}
{{#isEqualOr questionType 3}}
{{# questionContents}}
<dd><textarea class="text-input"></textarea></dd>
{{/ questionContents}}
{{/isEqualOr}}
</dl>
{{/ questions}}
</div>
<div class="submit">
<button class="submit-btn">提交</button>
</div>
</div>
{{/ detail}}
<div class="qs-err">
<p class="err-text">{{{errText}}}</p>
<a href="/3party/questionnaire">去做问卷</a>
</div>
<div id="tip-dialog" class="tip-dialog hide">
<div class="dg-wrap">
<div class="dg-content"><p>您的问卷还没有成功提交哦!<br>是否立刻返回!</p></div>
<div class="dg-btns clearfix">
<span class="back-btn">返回</span>
<span class="close-btn continue-btn">继续填写</span>
</div>
</div>
</div>
</div>
... ...
<div class="qs-list-page">
{{#if list}}
<ul id="qs-list" class="qs-list">
{{#each list}}
<li data-id="{{id}}" data-title="{{title}}" data-desc="{{share.subtitle}}" data-img="{{share.imgUrl}}">{{index}}.{{title}}</li>
{{/each}}
</ul>
{{/if}}
<div id="tip-dialog" class="tip-dialog hide">
<div class="dg-wrap">
<div class="dg-content"></div>
<div class="dg-btns sure-btns clearfix hide">
<span class="close-btn sure-btn">确定</span>
</div>
<div class="dg-btns share-btns clearfix">
<span class="close-btn cancel-btn">取消</span>
<span class="share-btn">分享问卷</span>
</div>
</div>
</div>
</div>
... ...
... ... @@ -2,6 +2,9 @@
const model = require('../models/feature');
exports.index = function(req, res, next) {
// 唤起 APP 的路径
res.locals.appPath = `yohobuy://yohobuy.com/goapp?openby:yohobuy={"action":"go.h5","params":{"param":{"share_id":"${req.query.share_id}"},"share":"/operations/api/v5/webshare/getShare","shareparam":{"share_id":"${req.query.share_id}"},"url":"https://activity.yoho.cn/feature/${req.params.code}.html"}}`;
model.index({
code: req.params.code,
type: req.query.type
... ...
... ... @@ -28,4 +28,12 @@
<span>400-889-9646</span>
<i class="arr-ico iconfont">&#xe604;</i>
</a>
<a class="list clearfix" href="/3party/questionnaire">
<i class="questionnaire-ico icon"></i>
<div>
<p class="title">调研中心</p>
<p class="tip">一起来参与,赢取更多惊喜!</p>
</div>
<i class="arr-ico iconfont">&#xe604;</i>
</a>
</div>
... ...
... ... @@ -51,6 +51,7 @@ module.exports = () => {
yoho.isApp = (req.query.app_version && req.query.app_version !== 'false') ||
(req.query.appVersion && req.query.appVersion !== 'false') ||
req.cookies.app_version || /YohoBuy/i.test(req.get('User-Agent') || '');
yoho.isMobile = /(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\.browser|up\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\s*mobi|opera\*mini|320x320|240x320|176x220)/i.test(req.get('User-Agent') || ''); // eslint-disable-line
yoho.isWechat = /micromessenger/i.test(req.get('User-Agent') || '');
yoho.isWeibo = ua.indexOf('weibo') !== -1;
yoho.isqq = /MQQBrowser/i.test(req.get('User-Agent') || '');
... ...
... ... @@ -4,7 +4,7 @@ const path = require('path');
const info = {
host: '127.0.0.1',
port: 5001,
publicPath: 'http://127.0.0.1:5001'
publicPath: 'http://127.0.0.1:5001/'
};
try {
... ...
require('3party/question-detail.page.css');
let $ = require('yoho-jquery'),
yoho = require('yoho-app'),
tipDg = require('plugin/tip'),
share = require('common/share');
let question = {
$base: $('#qs-wrap'),
init: function() {
let that = this;
this.$errTip = $('.error-tip');
this.$item = $('.qs-item', this.$base);
this.startTime = Date.parse(new Date()) / 1000;
if (this.$base.length) {
let data = this.$base.data();
this.shareInfo = {
title: data.title,
link: 'http://m.yohobuy.com/3party/questionnaire/' + data.id,
desc: data.desc,
imgUrl: data.img
};
// 设置分享信息
share(this.shareInfo);
// 设置app页面信息及分享信息
if (yoho.isApp) {
yoho.ready(function() {
yoho.invokeMethod('get.pageType', {
pageType: 'questionnaire'
});
yoho.invokeMethod('set.shareInfo', that.shareInfo);
});
}
}
this.bindEvent();
},
bindEvent: function() {
let that = this;
this.$base.on('click', '.radio-option', function() {
let $this = $(this);
if (!$this.hasClass('on')) {
$this.siblings('.on').removeClass('on');
$this.addClass('on');
}
}).on('click', '.check-option', function() {
let $this = $(this);
if ($this.hasClass('on')) {
$this.removeClass('on');
} else {
$this.addClass('on');
}
}).on('click', 'input', function(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
window.event.cancelBubble = true;
}
});
$('.submit-btn').click(function() {
that.saveAnswers(that.packAnswersInfo());
});
},
packAnswersInfo: function() {
let that = this;
let answer = [];
let $errDom;
this.$item.each(function() {
if ($errDom) {
return;
}
let $this = $(this);
let data = $this.data();
let ans = [];
let errText = '';
if (+data.type === 3) {
$this.find('.text-input').each(function() {
let val = $.trim($(this).val());
if (val) {
ans.push({
questionIndex: data.index,
answerIndex: ans.length,
addon: val
});
}
if (val.length > 400) {
errText = '输入内容过长';
}
});
} else {
$this.find('.on').each(function() {
let $that = $(this),
$input = $that.find('input'),
a = {
questionIndex: data.index,
answerIndex: $that.data('index')
};
if ($input && $input.length) {
a.addon = $input.val();
}
ans.push(a);
});
if (data.type === '1') {
ans.length = 1;
}
}
if (errText || !ans.length) {
$errDom = $this;
if (!errText) {
errText = +data.type === 3 ? '请填写一条回答' : '请选择一个选项';
}
that.showError(errText, $errDom);
} else {
answer = $.merge(answer, ans);
}
});
if ($errDom) {
return [];
} else {
return answer;
}
},
showError: function(tip, $errDom) {
let that = this;
this.$errTip.html(tip);
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(function() {
that.$errTip.empty();
}, 5000);
if ($errDom) {
let offTop = $errDom.offset().top,
errHeight = this.$errTip.outerHeight();
$('html,body').animate({scrollTop: offTop - errHeight}, 500);
}
},
saveAnswers: function(info) {
if (!info || !info.length) {
return;
}
$.ajax({
type: 'POST',
url: '/3party/questionnaire/submit',
data: {
id: this.$base.data('id'),
uid: this.$base.data('cid'),
startTime: this.startTime,
endTime: Date.parse(new Date()) / 1000,
frontAnswers: JSON.stringify(info)
}
}).then(function(data) {
if (data.code === 200) {
tipDg.show('调查问卷已成功提交,感谢您的帮助!');
setTimeout(function() {
if (yoho.isApp) {
yoho.invokeMethod('go.back');
} else {
window.history.go(-1);
}
}, 2000);
} else {
tipDg.show(data.message || '网络出了点问题~');
}
});
}
};
let tipDialog = {
$base: $('#tip-dialog'),
init: function() {
var that = this;
this.$base.on('click', '.close-btn', function() {
that.hide();
});
this.$base.on('click', '.back-btn', function() {
window.history.go(-1);
});
},
show: function() {
this.$base.removeClass('hide');
},
hide: function() {
this.$base.addClass('hide');
}
};
tipDialog.init();
question.init();
if (question.$base.length) {
$('.nav-back').removeAttr('href').click(function() {
tipDialog.show();
});
} else if (yoho.isApp) {
$('.qs-err a').removeAttr('href').click(function() {
yoho.invokeMethod('go.back');
});
}
... ...
'use strict';
require('3party/question-list.page.css');
let $ = require('yoho-jquery'),
yoho = require('yoho-app');
const DETAIL_URI = 'http://m.yohobuy.com/3party/questionnaire';
require('../common');
let qs = window.queryString;
function getQuestionStatus(reqData, cb) {
if (qs.uid) {
reqData.uid = qs.uid;
}
$.ajax({
type: 'POST',
url: '/3party/questionnaire/check',
data: reqData
}).then(function(data) {
if (cb && typeof cb === 'function') {
return cb(data);
}
});
}
function jumpQuestionDetail(data) {
let href;
if (qs && qs.uid && yoho.isApp) {
href = DETAIL_URI + '/' + data.id + '?uid=' + qs.uid;
} else {
href = DETAIL_URI + '/' + data.id;
}
if (yoho && yoho.isApp) {
let link = yoho.parseUrl(href);
yoho.goH5(href, JSON.stringify({
action: 'go.h5',
params: {
islogin: 'N',
type: 14,
updateflag: Date.now() + '',
url: link.path,
param: link.query
}
}));
} else {
window.location.href = href;
}
}
let tipDialog = {
$base: $('#tip-dialog'),
init: function() {
let that = this;
this.$content = $('.dg-content', this.$base);
this.$sureBtns = $('.sure-btns', this.$base);
this.$shareBtns = $('.share-btns', this.$base);
this.$base.on('click', '.close-btn', function() {
that.hide();
});
this.$base.on('click', '.share-btn', function() {
if (that.share && typeof that.share === 'function') {
that.share();
} else {
that.hide();
}
});
},
show: function(info) {
this.share = false;
if (typeof info === 'object') {
this.$content.html(info.content);
this.$sureBtns.addClass('hide');
this.$shareBtns.removeClass('hide');
} else if (typeof info === 'string') {
this.$content.html('<p>' + info + '</p>');
this.$sureBtns.removeClass('hide');
this.$shareBtns.addClass('hide');
} else {
return;
}
this.$base.removeClass('hide');
},
hide: function() {
this.$base.addClass('hide');
}
};
tipDialog.init();
let $list = $('#qs-list');
$list.on('click', 'li', function() {
let data = $(this).data();
if (!data.id) {
return;
}
getQuestionStatus({uid: qs.uid, id: data.id}, function(resData) {
if (resData.code === 200) {
jumpQuestionDetail(data);
} else if (resData.code === 206) {
if (yoho && yoho.isApp) {
yoho.invokeMethod('go.showShareAlert', {
title: data.title,
link: 'http://m.yohobuy.com/3party/questionnaire/' + data.id,
desc: data.desc,
imgUrl: data.img
});
} else {
tipDialog.show('调查问卷已成功提交,<br>感谢您的帮助!');
}
} else if (resData.message) {
if (yoho && yoho.isApp) {
yoho.goH5(DETAIL_URI + '/0');
} else {
window.location.href = DETAIL_URI + '/0';
}
}
});
});
if ($list.children().length === 1) {
let data = $list.children().first().data();
getQuestionStatus({uid: qs.uid, id: data.id}, function(resData) {
if (resData.code === 200) {
jumpQuestionDetail(data);
}
});
}
... ...
body {
background: #f0f0f0;
}
.nav-home {
display: none !important;
}
.qs-detail-page {
font-size: 28px;
$borderColor: #ececec;
.error-tip {
width: 100%;
background-color: #ff7e82;
color: #fff;
padding-left: 30px;
line-height: 50px;
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
.sub-title {
line-height: 2;
padding: 14px 20px;
background: #fff;
border-bottom: 1px solid $borderColor;
text-align: center;
}
.guide-tip {
line-height: 2;
padding: 0 30px;
color: #555;
}
.qs-item {
background: #fff;
margin-bottom: 30px;
padding-left: 30px;
border-top: 1px solid $borderColor;
border-bottom: 1px solid $borderColor;
> * {
line-height: 60px;
padding: 10px 0;
}
> dd {
border-top: 1px solid $borderColor;
color: #b4b4b4;
input {
width: calc(100% - 60px);
height: 50px;
margin-left: 34px;
border: 0;
border: 1px solid $borderColor;
color: #444;
}
&.on {
color: #444;
}
}
textarea {
width: calc(100% - 26px);
height: 100px;
resize: none;
border: 1px solid #ddd;
display: block;
color: #444;
}
.iconfont {
display: inline-block;
width: 34px;
height: 34px;
line-height: 34px;
border: 1px solid #b4b4b4;
vertical-align: middle;
font-size: 18px;
text-align: center;
color: #fff;
position: absolute;
margin-top: 13px;
}
.option-box {
padding-left: 40px;
img {
display: inline-block;
vertical-align: middle;
}
}
.radio-option .iconfont {
border-radius: 17px;
overflow: hidden;
}
.on .iconfont {
border-color: #444;
background-color: #444;
}
}
.submit {
padding: 0 20px;
.submit-btn {
width: 100%;
height: 80px;
background: #3e3e3e;
color: #fff;
border: 0;
border-radius: 4px;
margin-bottom: 20px;
outline: none;
}
}
.detail-wrap + .qs-err {
display: none;
}
.qs-err {
&:before {
content: "";
width: 300px;
height: 190px;
background: resolve("3party/qs-lose.jpg") no-repeat;
display: block;
margin: 120px auto 0;
background-size: 100%;
}
.err-text {
width: 56%;
line-height: 1.5;
color: #444;
margin: 0 auto;
padding-bottom: 50px;
display: block;
text-align: center;
}
a {
width: 60%;
height: 80px;
line-height: 80px;
margin: 0 auto;
background: #3e3e3e;
color: #fff;
display: block;
text-align: center;
border-radius: 4px;
}
}
.tip-dialog {
width: 100%;
height: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 10;
&:before {
content: "";
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
z-index: -1;
}
.dg-wrap {
width: 70%;
background: #fff;
border-radius: 6px;
position: absolute;
left: 15%;
top: 50%;
margin-top: -95px;
}
.dg-content {
height: 140px;
line-height: 140px;
> p {
width: 100%;
line-height: 1.4;
display: inline-block;
text-align: center;
vertical-align: middle;
}
}
.dg-btns {
border-top: 1px solid #efefef;
> * {
width: 50%;
line-height: 60px;
text-align: center;
display: block;
float: left;
cursor: pointer;
box-sizing: border-box;
}
}
.continue-btn {
border-left: 1px solid #efefef;
box-sizing: border-box;
color: #d90005;
}
}
}
... ...
.nav-home {
display: none !important;
}
.qs-list-page {
font-size: 28px;
$borderColor: #ececec;
.qs-list {
line-height: 90px;
color: #444;
padding-left: 30px;
border-bottom: 1px solid $borderColor;
> li {
border-top: 1px solid $borderColor;
}
}
.tip-dialog {
width: 100%;
height: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 10;
&:before {
content: "";
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
z-index: -1;
}
.dg-wrap {
width: 70%;
background: #fff;
border-radius: 6px;
position: absolute;
left: 15%;
top: 50%;
margin-top: -95px;
}
.dg-content {
height: 140px;
line-height: 140px;
> p {
width: 100%;
line-height: 1.4;
display: inline-block;
text-align: center;
vertical-align: middle;
}
}
.dg-btns {
border-top: 1px solid #efefef;
> * {
width: 50%;
line-height: 60px;
text-align: center;
display: block;
float: left;
cursor: pointer;
}
}
.dg-btns .sure-btn {
width: 100%;
}
.share-btn {
border-left: 1px solid #efefef;
box-sizing: border-box;
color: #d90005;
}
}
}
... ...
... ... @@ -124,6 +124,13 @@
margin: 30px 20px;
}
.questionnaire-ico {
background-image: url("/service/chat/questionnaire-ico.png");
width: 60px;
height: 60px;
margin: 30px 20px;
}
.arr-ico {
line-height: 120px;
color: #e1e1e1;
... ...