Authored by 王水玲

店铺推荐 为您优选

... ... @@ -8,6 +8,18 @@
const _ = require('lodash');
const item = require('../models/item');
// 保存在 gids 和 skns ,最近流览功能
const saveRecentGoodInCookies = (oldSkns, res, addSkns) => {
oldSkns = oldSkns ? oldSkns.split(',') : [];
oldSkns = _.reject(oldSkns, old => old === String(addSkns) ? true : false);
oldSkns.unshift(addSkns);
res.cookie('_browseskn', oldSkns.splice(0, 30).join(','), {
maxAge: 2000000000,
domain: '.yohoblk.com'
});
};
/**
* 商品详情页
* @function index
... ... @@ -18,6 +30,12 @@ const index = (req, res, next) => {
if (_.isEmpty(result)) {
return next();
}
// 最近浏览存储cookies
if (!_.has(result.content.goodInfo, 'fashionTopGoods')) {
saveRecentGoodInCookies(req.cookies._browseskn, res, result.content.goodInfo.productSkn);
}
result.page = 'item';
result.title += ` | ${res.locals.title}`;
res.display('item', result);
... ...
... ... @@ -5,15 +5,112 @@
*/
'use strict';
const _ = require('lodash');
const recommend = require('../models/recommend');
const _ = require('lodash');
const md5 = require('md5');
const requestIp = require('request-ip');
const _channel = (channel) => {
let yhChannel = 1;
switch (channel) {
case 'men':
yhChannel = 1;
break;
case 'women':
yhChannel = 2;
break;
case 'lifestyle':
yhChannel = 4;
break;
default:
break;
}
return yhChannel;
};
/**
* 获取客户端唯一标识
* @return string
*/
const getUdid = (req, res) => {
let udid = req.cookies.udid;
if (!udid) {
udid = md5(req.ip || requestIp.getClientIp(req));
if (res && res.cookie) {
res.cookie('udid', udid);
}
}
return udid;
};
/**
* 为您优选
* @param req
* @param res
* @param next
*/
const getRecommendProduct = (req, res, next) => {
recommend.getRecommendProduct().then((result) => {
let params = {
yh_channel: _channel(req.query.channel || req.cookies._Channel || 'men'),
udid: getUdid(req, res)
};
recommend.getRecommendProduct(params).then((result) => {
res.json(result);
});
}).catch(next);
};
/**
* 店铺推荐
* @param req
* @param res
* @param next
*/
const getRecommendShop = (req, res, next) => {
recommend.getRecommendShop(req.query.productSkn).then((result) => {
res.json(result);
}).catch(next);
};
/**
* 最近浏览
* @param req
* @param res
* @param next
*/
const recentPreview = (req, res, next) => {
let browserSkn = req.cookies._browseskn;
let limit = req.query.limit;
// 拆解skn
let skn = browserSkn ? decodeURIComponent(browserSkn).replace(/\-(\d)+(\,){0,1}/g, ',') : '';
// 去除skn字符串的最后一个多余的,
if (skn && skn.lastIndexOf(',') === skn.length - 1) {
skn = skn.slice(0, -1);
}
if (!skn) {
res.jsonp({
code: 200,
data: [],
message: 'User info'
});
} else {
skn = _.slice(_.uniq(skn.split(',')), 0, limit).join(','); // 去重+截取
recommend.recentPreview(skn, limit).then(data => {
res.jsonp(data);
}).catch(next);
}
};
module.exports = {
getRecommendProduct
getRecommendProduct,
getRecommendShop,
recentPreview
};
... ...
... ... @@ -320,6 +320,7 @@ const setBrandBanner = (base, brand, shop) => {
const setProductData = base => {
let resData = {
id: base.product_id,
productSkn: base.product_skn,
name: base.product_name,
brandName: _.has(base, 'brand_info.brand_name') ? base.brand_info.brand_name : '',
intro: base.sales_phrase,
... ...
... ... @@ -6,29 +6,96 @@
'use strict';
const _ = require('lodash');
const api = global.yoho.API;
const logger = global.yoho.logger;
const _ = require('lodash');
const config = global.yoho.config;
const getRecommendProduct = (params) => {
params = {
yh_channel: 4,
udid: '0f528764d624db129b32c21fbca0cb8d6',
limit: 5,
rec_pos: 100003
};
return api.get('app.home.newPreference', params).then((data) => {
if (data.code === 200 && data.data.length > 0) {
console.log(data);
return data.data;
} else {
logger.error('get recommend product data code is not 200');
return [];
/**
* 根据性别来决定 默认图片获取字段 如果是 2、3
*
* 则优先从cover_2 --》 cover_1 -- 》 images_url
* 否则优先从cover_1 --》 cover_2 -- 》 images_url
*
*/
const _procProductImg = (product) => {
if (product.gender === '2,3' || product.gender === '2' || product.gender === '3') {
return product.cover_2 || product.images_url || product.cover_1 || '';
}
return product.cover_1 || product.images_url || product.cover_2 || '';
};
const _processProduct = (list) => {
let newRes = [];
list = list || [];
list.forEach(item => {
let defaultGoods = _.find(item.goods_list, {is_default: 'Y'});
// 无默认商品取商品列表第一个
if (!defaultGoods) {
defaultGoods = item.goods_list[0];
}
item.default_images = _procProductImg(defaultGoods);
item.goodsUrl = `${config.siteUrl}/product/pro_${item.product_id}_${defaultGoods.goods_id}/${item.cn_alphabet}.html`; // eslint-disable-line
});
_.chunk(list, 5).forEach(item => {
newRes.push({
list: item
});
});
return newRes;
};
const getRecommendProduct = (params, uid) => {
if (uid !== 0 && uid !== null) {
params.uid = uid;
}
return api.get('', _.assign({
method: 'app.home.newPreference',
limit: 20,
page: 1,
rec_pos: 100003
}, params), {
code: 200,
catch: true
}).then(data => {
return _processProduct(data.data.product_list);
});
};
const getRecommendShop = (productSkn) => {
return api.get('', {
method: 'web.product.shopRecommend',
limit: 20,
page: 1,
product_skn: productSkn
}, {
code: 200
}).then(data => {
return _processProduct(data.data.product_list);
});
};
const recentPreview = (skn, limit) => {
return api.get('', {
method: 'h5.product.batch',
productSkn: skn,
limit: limit,
page: 1
}, {
code: 200
}).then(data => {
return _processProduct(data.data.product_list);
});
};
module.exports = {
getRecommendProduct
getRecommendProduct,
getRecommendShop,
recentPreview
};
... ...
... ... @@ -33,5 +33,7 @@ router.post('/brand/togglecollect', auth, fav.brand);
router.get('/query', query.index);
router.get('/getRecommendProduct', recommend.getRecommendProduct);
router.get('/getRecommendShop', recommend.getRecommendShop);
router.get('/recentPreview', recommend.recentPreview);
module.exports = router;
... ...
... ... @@ -26,6 +26,11 @@ module.exports = {
api: 'http://api.yoho.yohoops.org/',
service: 'http://service.yoho.yohoops.org/',
search: 'http://search.yohoops.org/yohosearch/'
// api: 'http://api-test3.yohops.com:9999/',
// service: 'http://service-test3.yohops.com:9999/',
// liveApi: 'http://testapi.live.yohops.com:9999/',
// singleApi: 'http://api-test3.yohops.com:9999/'
},
useOneapm: false,
useCache: false,
... ...
... ... @@ -95,6 +95,7 @@
"postcss-sprites": "^3.1.2",
"postcss-use": "^2.0.2",
"precss": "^1.4.0",
"request-ip": "^1.2.3",
"rewire": "^2.5.1",
"shelljs": "^0.7.0",
"stylelint": "^7.1.0",
... ...
/**
* 链接改成自适应链接
* @return {[type]}
*/
module.exports = function(url) {
return url.replace('http:', '');
};
... ...
... ... @@ -49,8 +49,12 @@ require('../plugins/share');
lazyload($('img.lazy'));
recProduct.init({
url: '/product/getRecommendShop',
dom: '.recommend-product',
isGoodsDetail: true
isGoodsDetail: true,
params: {
productSkn: '51085370'
}
});
function changeThumb($dom) {
... ...
... ... @@ -3,37 +3,112 @@
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/11/21
*/
var recProduct = require('../../../tpl/product/recommend-product.hbs');
var recTemplet = require('../../../tpl/product/recommend-templet.hbs');
var recProduct = require('../../../tpl/product/recommend-product.hbs'),
recTemplet = require('../../../tpl/product/recommend-templet.hbs');
var recommmendProduct = {
init: function(params) {
var _this = this;
var obj = {
isGoodsDetail: false
};
recommendPage: 0,
recentPreviewPage: 0,
init: function(obj) {
var _this = this,
params = obj.params || {},
$dom = $(obj.dom),
url = obj.url || '/product/getRecommendProduct';
if (params.isGoodsDetail) {
obj.isGoodsDetail = true;
}
$dom.append(recTemplet({
isGoodsDetail: obj.isGoodsDetail ? true : false
}));
$.extend(_this, {
$changeBtn: $dom.find('.change-btn'),
$dom: $dom
});
_this.getProducts({
url: url,
index: 0,
params: params
});
$dom.find('.head-tab').on('click', function() {
var index = $(this).index(),
$tabCont = $('.tab-cont-' + index);
$('.head-tab').removeClass('active');
$(this).addClass('active');
if ($tabCont.find('.goods-group').length === 0 && index === 1) {
_this.getProducts({
url: '/product/recentPreview',
index: index,
params: {
limit: 20
}
});
}
_this.isShowChangeBtn($tabCont.find('.goods-group').length);
$tabCont.removeClass('hide').siblings().addClass('hide');
});
_this.$changeBtn.on('click', function() {
var index = $('.recommend-header .active').index(),
$goodsGroup = $('.tab-cont-' + index).find('.goods-group'),
currPage = 0;
$(params.dom).append(recTemplet(obj));
if ($goodsGroup.length > 1) {
if (index === 1) {
currPage = _this.countPage(_this.recentPreviewPage, $goodsGroup);
_this.recentPreviewPage = currPage;
} else {
currPage = _this.countPage(_this.recommendPage, $goodsGroup);
_this.recommendPage = currPage;
}
}
_this.getRecommendProduct({
page: 1,
dom: params.dom
$goodsGroup.eq(currPage).show().siblings().hide();
_this.lazyImage();
});
},
getRecommendProduct: function(params) {
$.get('/product/getRecommendProduct', {
page: params.page
}, function(data) {
if (data) {
$(params.dom).find('.goods-area').html(recProduct(data));
countPage: function(page, $goodsGroup) {
var currPage = 0;
page++;
if (page > $goodsGroup.length - 1) {
currPage = 0;
} else {
currPage = page;
}
return currPage;
},
getProducts: function(obj) {
var _this = this;
$.get(obj.url, obj.params, function(result) {
if (result) {
_this.$dom.find('.tab-cont-' + obj.index).html(recProduct({result: result}));
_this.lazyImage();
_this.isShowChangeBtn(result.length);
}
});
},
isShowChangeBtn: function(currGoodsLen) {
if (currGoodsLen <= 1) {
this.$changeBtn.hide();
} else {
this.$changeBtn.show();
}
},
lazyImage: function() {
$('img.lazy').lazyload({
effect: 'fadeIn'
});
}
};
require('yoho-jquery-lazyload');
module.exports = recommmendProduct;
... ...
@import 'list';
@import 'item';
@import '_shop.css';
@import "list";
@import "item";
@import "_shop.css";
/* 组件 */
@import 'brand-banner';
@import "brand-banner";
@import "recommend-product";
... ...
... ... @@ -478,55 +478,6 @@
}
}
.recommend-product {
width: 100%;
margin-bottom: 55px;
.recommend-header {
height: 40px;
border-bottom: 1px solid #ddd;
}
.head-tab {
width: 140px;
height:40px;
line-height: 42px;
font-size: 14px;
text-align: center;
float: left;
cursor: pointer;
&.active {
font-weight: bold;
border: 1px solid #ddd;
border-bottom-color: #fff;
box-sizing: border-box;
}
}
.change-btn {
float: right;
font-size: 14px;
font-weight: bold;
line-height: 42px;
.iconfont {
font-size: 18px;
position: relative;
top: 2px;
margin-left: 5px;
}
}
.product-area {
img {
width: 214px;
height: 288px;
background: #f5f7f6;
}
}
}
.info-block {
border: 1px solid #f3f3f3;
padding: 40px 130px;
... ...
.recommend-product {
width: 100%;
margin-bottom: 55px;
.recommend-header {
height: 40px;
border-bottom: 1px solid #ddd;
}
.head-tab {
width: 140px;
height: 40px;
line-height: 42px;
font-size: 14px;
text-align: center;
float: left;
cursor: pointer;
&.active {
font-weight: bold;
border: 1px solid #ddd;
border-bottom-color: #fff;
box-sizing: border-box;
}
}
.change-btn {
float: right;
font-size: 14px;
font-weight: bold;
line-height: 42px;
cursor: pointer;
.iconfont {
font-size: 18px;
position: relative;
top: 2px;
margin-left: 5px;
}
}
.product-area {
img {
width: 214px;
height: 288px;
background: #f5f7f6;
}
}
.recommend-goods {
width: 100%;
margin: 0 auto;
.goods-group {
display: none;
&:first-child {
display: block;
}
}
.goods {
width: 214px;
float: left;
margin: 20px 0 0 20px;
&:first-child {
margin-left: 0;
}
.goods-img,
img {
width: 214px;
height: 288px;
}
.goods-brand {
font-size: 14px;
font-weight: bold;
text-align: center;
padding: 15px 0;
width: 214px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.goods-name {
font-size: 12px;
text-align: center;
width: 214px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.goods-price {
font-size: 16px;
font-weight: bold;
text-align: center;
padding: 15px 0;
b {
font-size: 12px;
color: #878787;
margin-left: 15px;
text-decoration: line-through;
}
}
}
}
}
... ...
{{#each products}}
<div class="goods" data-id="{{productId}}" data-url="{{url}}">
<div class="goods-img">
<a href="{{https url}}" target="_blank">
<img class="lazy" data-original="{{image defaultImages 263 351}}" width="263" height="351" alt="">
</a>
</div>
<div class="goods-brand">{{brandName}}</div>
<div class="goods-name">{{productName}}</div>
<div class="goods-price">
<span>¥{{round salesPrice}}</span>
</div>
{{#each result}}
<div class="goods-group">
{{#list}}
<div class="goods">
<a href="{{goodsUrl}}" target="_blank">
<div class="goods-img">
<img class="lazy" src="" data-original="{{image default_images 214 288}}" width="214" height="288" alt="">
</div>
<div class="goods-brand">{{brand_name}}</div>
<div class="goods-name">{{product_name}}</div>
<div class="goods-price">
<span>¥{{round sales_price}}</span>
{{#if market_price}}
<b>¥{{round market_price}}</b>
{{/if}}
</div>
</a>
</div>
{{/list}}
</div>
{{/each}}
... ...
... ... @@ -3,4 +3,7 @@
<span class="head-tab">最近浏览</span>
<span class="change-btn">换一批<i class="iconfont">&#xe613;</i></span>
</div>
<div class="goods-area clearfix"></div>
<div class="recommend-goods clearfix">
<div class="tab-cont-0"></div>
<div class="tab-cont-1 hide"></div>
</div>
... ...