Authored by 毕凯

Merge branch 'feature/material' into 'master'

Feature/material



See merge request !675
'use strict';
const materialModel = require('../models/material-new');
const headerModel = require('../../../doraemon/models/header'); // 头部model
exports.list = (req, res, next) => {
let responseData = {
pageHeader: headerModel.setNav({
navTitle: '商品素材列表页',
}),
title: '商品素材列表页',
module: '3party',
page: 'material-new',
width750: true,
localCss: true
};
let params = {
uid: req.user.uid,
page: 1,
isApp: req.yoho.isApp,
unionType: req.query.union_type
};
req.ctx(materialModel).canLogin(params).then(result => {
if (result === 'N') {
return next();
} else {
req.ctx(materialModel).list(params).then(list => {
res.render('material-new', Object.assign(responseData, list));
}).catch(next);
}
}).catch(next);
};
... ...
... ... @@ -45,7 +45,7 @@ exports.submit = (req, res, next) => {
// 标识问卷来源
if (req.yoho.isApp) {
params.sourceType = 'APP';
} else if (req.yoho.mobile) {
} else if (req.yoho.isMobile) {
params.sourceType = 'H5';
} else {
params.sourceType = 'PC';
... ...
'use strict';
const platformApi = new global.yoho.ApiBase(global.yoho.config.domains.platformApi, {
name: 'imCs',
cache: global.yoho.cache,
useCache: false
});
const _ = require('lodash');
const utils = '../../../utils';
const productProcess = require(`${utils}/product-process`);
class materialModel extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
}
canLogin(params) {
return platformApi.get('/platform/product/material/canlogin', {
uid: params.uid
}).then(result => {
if (result && result.code === 200) {
return result.data.canLogin;
}
});
}
list(params) {
let options = {
data: {
method: 'app.search.recommendProduct',
type: 'default',
limit: 24
},
param: {
code: 200
}
};
return this.get(options).then(result => {
if (result && result.code === 200) {
let resu = {
goods: []
};
if (_.get(result, 'data.product_list', [])) {
let build = [];
build = productProcess.processProductList(result.data.product_list || [], {
isApp: params.isApp,
showSimilar: false
});
if (params.unionType) {
_.forEach(build, (val) => {
let newUrl = val.url;
val.url = newUrl.replace('.html', `.html?union_type=${params.unionType}`);
});
}
resu.goods = build;
}
return resu;
}
});
}
}
module.exports = materialModel;
... ...
... ... @@ -6,6 +6,32 @@
const _ = require('lodash');
const _fillQuestionContents = data => {
if (data && +data.questionType === 3) {
data.questionContents = _.fill(Array(data.fillBlankNum || 1), true);
}
return data;
};
const _handelSubQuestion = (qs, jid) => {
if (qs) {
qs = _.concat([], qs);
_.forEach(qs, value => {
Object.assign(value, {
subQs: true,
jid: jid
});
_fillQuestionContents(value);
});
}
return qs;
};
module.exports = class extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
... ... @@ -25,7 +51,7 @@ module.exports = class extends global.yoho.BaseModel {
}
});
return list;
return _.isEmpty(list) ? false : list;
});
}
getQuestionStatus(params) {
... ... @@ -54,10 +80,21 @@ module.exports = class extends global.yoho.BaseModel {
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);
_.forEach(data.questions, (value, key) => {
if (!_.isEmpty(value.questionContents)) {
_.forEach(value.questionContents, (subval, subkey) => {
if (!subval.jumpQuestion) {
return;
}
value.hasSub = true;
subval.jid = `${key}-${subkey}`;
subval.jumpQuestion = _handelSubQuestion(subval.jumpQuestion,
subval.jid);
});
}
_fillQuestionContents(value);
});
}
... ...
... ... @@ -13,7 +13,9 @@ const check = require(`${cRoot}/check`);
const question = require(`${cRoot}/question`);
const validateCode = require('../passport/controllers/validateCode');
const auth = require('../../doraemon/middleware/auth');
const material = require(`${cRoot}/material`);
// const material = require(`${cRoot}/material`);
const materialNew = require(`${cRoot}/material-new`);
// routers
... ... @@ -26,7 +28,6 @@ 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);
router.get('/material', auth, material.list);
router.get('/material/moreGoods', auth, material.moreGoods);
router.get('/material', auth, materialNew.list);
module.exports = router;
... ...
<div class="material-c reds-shop">
<div class="filter-box">
{{> product/filter-tab}}
{{> common/filter}}
</div>
<div id="all-goods" class="tab-panel active">
<div class="good-list-page yoho-page">
<div id="goods-container" class="goods-container">
<div class="default-goods container clearfix">
{{#goods}}
{{> common/goods}}
{{/goods}}
</div>
</div>
</div>
</div>
<input type="hidden" id="material-flag" value="material" />
</div>
... ...
... ... @@ -7,38 +7,17 @@
<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}}
{{> question/item}}
{{#isEqualOr questionType 3}}
{{#if hasSub}}
<div class="sub-qs-wrap">
{{# questionContents}}
<dd><textarea class="text-input"></textarea></dd>
{{# jumpQuestion}}
{{> question/item}}
{{/ jumpQuestion}}
{{/ questionContents}}
{{/isEqualOr}}
</dl>
</div>
{{/if}}
{{/ questions}}
</div>
... ...
... ... @@ -5,6 +5,10 @@
<li data-id="{{id}}" data-title="{{title}}" data-desc="{{share.subtitle}}" data-img="{{share.imgUrl}}">{{index}}.{{title}}</li>
{{/each}}
</ul>
{{^}}
<div class="empty-list">
<p>调研问卷时间未开始,有货君正在生成调研问卷,<br>请您先逛一逛,稍后再来~~</p>
</div>
{{/if}}
<div id="tip-dialog" class="tip-dialog hide">
... ...
<dl class="{{#if subQs}}sub-qs-item sub-{{jid}} hide{{^}}qs-item{{/if}}" data-index="{{questionIndex}}" data-type="{{questionType}}">
<dt>{{questionTitle}}</dt>
{{#isEqualOr questionType 1}}
{{# questionContents}}
<dd class="radio-option" data-index="{{@index}}"{{#if jid}} data-jid="{{jid}}"{{/if}}>
<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}}"{{#if jid}} data-jid="{{jid}}"{{/if}}>
<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>
... ...
... ... @@ -187,6 +187,11 @@ const _searchGoods = (params) => {
method = 'app.search.coupon';
}
// 物料商品列表增加
if (params.material === 'true') {
method = 'app.search.recommendProduct';
}
return api.get('', _.assign({
method: method
}, params), {
... ... @@ -249,8 +254,15 @@ const getSearchData = (params) => {
newList.list = productProcess.processProductList(result.data.product_list || [], {
isApp: params.isApp || (params.appVersion && params.appVersion !== 'false'),
gender: _coverChannel[params.coverChannel],
showSimilar: params.shop_id ? false : true
showSimilar: params.shop_id || params.material === 'true' ? false : true
});
if (params.unionType && (params.material === 'true')) {
_.forEach(newList.list, (val) => {
let newUrl = val.url;
val.url = newUrl.replace('.html', `.html?union_type=${params.unionType}`);
});
}
if (result.data.rec_shop_list && result.data.rec_shop_list.length > 0 &&
!params.shop_id && !params.brand && !params.isApp) {
... ...
{
"name": "m-yohobuy-node",
"version": "5.8.2",
"version": "5.8.3",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ...
'use strict';
import {
Controller
} from 'yoho-mvc';
const allProduct = require('../../product/shop/all-product');
const lazyLoad = require('yoho-jquery-lazyload');
class MaterialController extends Controller {
constructor() {
super();
allProduct.getFilter();
lazyLoad($('img.lazy'));
}
}
module.exports = MaterialController;
... ...
require('3party/material-new.page.css');
const MaterialNewController = require('./controller');
new MaterialNewController();
... ...
... ... @@ -11,7 +11,7 @@ let question = {
let that = this;
this.$errTip = $('.error-tip');
this.$item = $('.qs-item', this.$base);
this.$item = $('.qs-item, .sub-qs-item', this.$base);
this.startTime = Date.parse(new Date()) / 1000;
if (this.$base.length) {
... ... @@ -44,11 +44,17 @@ let question = {
let that = this;
this.$base.on('click', '.radio-option', function() {
let $this = $(this);
let $this = $(this),
$par = $this.parent();
let jid = $this.data('jid');
if (!$this.hasClass('on')) {
$this.siblings('.on').removeClass('on');
$this.addClass('on');
if ($par.hasClass('qs-item')) {
that.setSubQuestion($par.next('.sub-qs-wrap'), jid);
}
}
}).on('click', '.check-option', function() {
let $this = $(this);
... ... @@ -65,21 +71,48 @@ let question = {
window.event.cancelBubble = true;
}
});
$('.submit-btn').click(function() {
that.saveAnswers(that.packAnswersInfo());
});
},
setSubQuestion: function($wrap, ids) {
if (!$wrap.length) {
return;
}
$wrap.slideUp();
$wrap.children().addClass('hide');
if (typeof ids === 'string' || typeof ids === 'number') {
ids = [ids];
}
if (!Array.isArray(ids)) {
return;
}
let i;
for (i = 0; i < ids.length; i++) {
if (ids[i]) {
$wrap.find('.sub-' + ids[i]).removeClass('hide');
}
}
$wrap.slideDown();
},
packAnswersInfo: function() {
let that = this;
let answer = [];
let $errDom;
this.$item.each(function() {
if ($errDom) {
let $this = $(this);
if ($errDom || $this.hasClass('hide')) {
return;
}
let $this = $(this);
let data = $this.data();
let ans = [];
let errText = '';
... ...
... ... @@ -44,6 +44,11 @@ let navInfo = {
sale: {
order: 0,
end: false
},
popularity: {
order: 0,
end: false,
type: 'popularity'
}
};
... ... @@ -54,6 +59,24 @@ let onSearching = false; // 是否正在搜索
let isScrollLoad = false; // 是否是滚动加载
let page = 1; // 页码
let nav;
let popularity = false;
let $dropList = $('.drop-list');
let $firstText = $('.first-li-more').find('.nav-txt');
let $thisLi = '';
// 物料商品列表标记
if ($('#material-flag').val() === 'material') {
let material = true;
popularity = true;
if ($('.first-li-more').hasClass('active')) {
page = 2; // 已在服务端渲染第一页
}
Object.assign(defaultOpt, {
material: material,
unionType: window.queryString.union_type
});
}
/**
* 处理筛选参数
... ... @@ -191,6 +214,18 @@ $listNav.bind('contextmenu', function() {
return false;
});
const popularityFilter = function(thisLi, type) {
if (thisLi.hasClass('active')) {
$dropList.hide();
return false;
}
$thisLi.addClass('active').siblings('li').removeClass('active');
defaultOpt.type = type;
Object.assign(defaultOpt, nav);
getGoodsList();
$dropList.hide();
};
$listNav.on('touchend touchcancel', function(e) {
page = 1;
beforeScroll = $(window).scrollTop();
... ... @@ -199,7 +234,24 @@ $listNav.on('touchend touchcancel', function(e) {
let $this = $(e.target).closest('li'); // 被点击的 Tab
let $active;
if ($this.hasClass('filter')) { // 筛选面板
// 默认列表增加人气筛选
if ($this.hasClass('default') && popularity) {
$this.siblings('.active').removeClass('active');
$this.addClass('active');
$dropList.toggle();
$dropList.find('.default').on('touchend touchcancel', function() {
$thisLi = $(this);
$firstText.html('默认');
popularityFilter($thisLi, 'default');
return false;
});
$dropList.find('.popularity').on('touchend touchcancel', function() {
$thisLi = $(this);
$firstText.html('人气');
popularityFilter($thisLi, 'popularity');
return false;
});
} else if ($this.hasClass('filter')) { // 筛选面板
// 筛选面板切换状态
if ($this.hasClass('active')) {
... ... @@ -216,6 +268,7 @@ $listNav.on('touchend touchcancel', function(e) {
filter.showFilter();
}
$dropList.hide().find('li').removeClass('active');
} else { // 排序改变
if ($this.hasClass('new')) {
... ... @@ -265,6 +318,7 @@ $listNav.on('touchend touchcancel', function(e) {
/* 排序条件更新 */
defaultOpt.type = navType;
Object.assign(defaultOpt, nav);
$dropList.hide().find('li').removeClass('active');
getGoodsList();
}
... ...
@import "common/good";
@import "common/filter";
@import "../product/shop/reds-shop/nav";
.reds-shop {
.filter-box {
display: block;
}
}
#all-goods {
position: relative;
}
.no-result-new {
text-align: center;
padding-top: 90px;
padding-bottom: 110px;
p {
color: #ccc;
margin-bottom: 25px;
font-size: 26px;
&:first-child {
color: #444;
font-size: 32px;
}
}
}
.search-divide {
clear: both;
height: 50px;
width: 100%;
padding: 10px 0;
color: #ccc;
text-align: center;
}
.material-c {
.drop-list {
padding-left: 30px;
position: absolute;
z-index: 10;
background: #fff;
width: 100%;
li {
width: 100%;
height: 88px;
font-size: 28px;
color: #b0b0b0;
line-height: 88px;
border-bottom: 1px solid #e0e0e0;
}
li:last-child {
border-bottom: none;
}
.active {
color: #444;
.chose {
display: inline-block;
width: 30px;
height: 22px;
background-image: resolve("product/chose.png");
background-size: 100%;
float: right;
margin-top: 33px;
margin-right: 30px;
background-repeat: no-repeat;
}
}
}
}
... ...
... ... @@ -36,6 +36,13 @@ body {
color: #555;
}
.sub-qs-wrap {
display: none;
position: relative;
z-index: 0;
}
.sub-qs-item,
.qs-item {
background: #fff;
margin-bottom: 30px;
... ...
... ... @@ -17,6 +17,14 @@
}
}
.empty-list {
font-size: 24px;
color: #bbb;
text-align: center;
padding-top: 360px;
line-height: 2;
}
.tip-dialog {
width: 100%;
height: 100%;
... ...
... ... @@ -41,6 +41,10 @@
&.cur {
color: #000;
}
&.drop {
color: #000;
}
}
}
... ...