Authored by htoooth

Merge branch 'master' into release/new-product-detail-api

... ... @@ -17,6 +17,16 @@ const shop = (shopId, req, res, next, brandInfo) => {
list.getShopInfo(shopId, req.user.id).then(shopInfo => {
if (+shopInfo.shopTemplateType === 2) { // 经典模板
let pjax = req.query._pjax;
if (pjax) {
list.getShopGoodsData(shopId, req.yoho.channel, req.query, shopInfo).then(result => {
Object.assign(result, {layout: false});
res.render('list/goods-list', result);
}).catch(next);
return;
}
list.getShopData(shopId, req.yoho.channel, req.query, shopInfo).then(result => {
Object.assign(result, {
page: 'shop',
... ...
... ... @@ -86,7 +86,7 @@ exports.suggest4Old = (req, res, next) => {
exports.serachFilterBrands = (req, res, next) => {
search.getBrands4Filter(Object.assign({}, req.query, {
keyword: req.query.query
keyword: req.query.query || ''
})).then(result => {
let dest = {
code: 200,
... ...
... ... @@ -14,6 +14,7 @@ const helpers = global.yoho.helpers;
const _ = require('lodash');
const limitNum = 60; // 商品每页显示数目
const needParams = ['query', 'msort', 'misort', 'gender', 'shelveTime'];
// 品牌页folder名称
const brandFolderSeries = '经典系列';
... ... @@ -97,9 +98,12 @@ const getListData = (params, channel) => {
if (result[1].code === 200) {
let dps = {};
if (params.gender) {
dps.gender = params.gender;
}
_.forEach(needParams, (value) => {
if (params[value]) {
dps[value] = params[value];
}
});
finalResult.list = Object.assign(
searchHandler.handlePathNavData(result[1].data.sort, params, 'sort', channel), {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
... ... @@ -168,9 +172,11 @@ const getListNewData = (params, channel) => {
};
let dps = {};
if (params.gender) {
dps.gender = params.gender;
}
_.forEach(needParams, (value) => {
if (params[value]) {
dps[value] = params[value];
}
});
// 获取左侧类目数据
if (result[1].code === 200) {
... ... @@ -279,9 +285,12 @@ const getBrandData = (params, extra, channel) => {
if (result[1].code === 200) {
let dps = {};
if (params.gender) {
dps.gender = params.gender;
}
_.forEach(needParams, (value) => {
if (params[value]) {
dps[value] = params[value];
}
});
Object.assign(finalResult.brand, {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
}, searchHandler.handlePathNavData(extra, params, 'brand', channel));
... ... @@ -663,6 +672,54 @@ const getShopData = (shopId, channel, params, shopInfo) => {
});
};
/**
* 获取店铺商品数据
*/
const getShopGoodsData = (shopId, channel, params) => {
let gender = _getGender(channel);
let resData = {};
_.unset(params, '_pjax');
return Promise.all([
searchApi.getProductList(Object.assign({
shop_id: shopId
}, params)), // 搜索店铺商品
searchApi.getShopBrands(shopId) // 店铺品牌数据
]).then(result => {
// 获取商品数据和顶部筛选条件
if (result[0].code === 200) {
Object.assign(resData, {
sort: searchHandler.handleOptsData(params, _.get(result[0], 'data.total', 0)),
list: productProcess.processProductList(_.get(result[0], 'data.product_list', []), {
newCoverSort: true,
showDiscount: false,
gender: gender
})
});
_.set(resData, 'sort.newPage', true); // 启用新的分页导航
}
let shopBrandIds = []; // 店铺的所有品牌id
if (result[1].code === 200 && result[1].data) {
_.forEach(result[1].data, value => {
shopBrandIds.push(value.brand_id);
});
}
// 根据品牌获取分类 (腾讯云测试没有该接口,暂时不调用分类)
return searchApi.getSortList({brand: shopBrandIds}).then(subRes => {
if (subRes.code === 200) {
let groupSort = _.get(subRes, 'data.sort', []);
Object.assign(resData, searchHandler.setShopSort(groupSort, params));
}
return resData;
});
});
};
const getShopListData = (channel, params, uid) => {
let gender = _getGender(channel),
shopId = params.shopId,
... ... @@ -732,7 +789,8 @@ const getShopListData = (channel, params, uid) => {
goods: productProcess.processProductList(goodsList, {
newCoverSort: true,
showDiscount: false,
gender: _getGender(channel)
gender: _getGender(channel),
query: params.query
}),
footPager: {tip: tip}
});
... ... @@ -803,7 +861,7 @@ const getBaseShopData = (params, extra, channel, shopId) => {
if (result[1].code === 200) {
let data = result[1].data || {},
decorator = shopHandler.getShopDecorator(data, {}, shopId);
decorator = shopHandler.getShopDecorator(data, {}, shopId, true);
Object.assign(decorator.shopTopBannerBase, {
shopId: shopId,
... ... @@ -813,6 +871,10 @@ const getBaseShopData = (params, extra, channel, shopId) => {
});
_.set(resData, 'brand.shopBanner', decorator.shopTopBannerBase);
_.unset(resData, 'brand.brandBanner');
if (decorator.signboard) {
_.set(resData, 'brand.signboard', decorator.signboard);
}
}
if (result[2].code === 200) {
... ... @@ -839,6 +901,7 @@ module.exports = {
getAdnav,
getShopInfo,
getShopData,
getShopGoodsData,
getShopListData,
getBaseShopData
};
... ...
... ... @@ -31,7 +31,6 @@ const yhChannel = {
* @return {[type]} [description]
*/
exports.getSaleGoodsList = (params) => {
let finalParams = {
method: 'app.search.sales',
limit: 60,
... ... @@ -39,13 +38,11 @@ exports.getSaleGoodsList = (params) => {
productSize: '384x511'
};
Object.assign(finalParams, params);
if (finalParams.order === '') {
delete finalParams.order;
if (params.channel) {
finalParams.yh_channel = yhChannel[params.channel].channel;
}
return api.get('', finalParams);
return api.get('', Object.assign(finalParams, params));
};
/**
... ...
... ... @@ -362,15 +362,18 @@ exports.getSaleDiscountData = (params, channel) => {
};
}
}
// 这里把 channel 全都处理掉,因为根据channel查小分类,而传入商品池时,不需要
return api.all([
saleApi.getSaleGoodsList({
channel: channel,
channel: null,
saleType: '3',
limit: '1',
productPool: result[1].data[0].product_pool,
order: ''
productPool: result[1].data[0].product_pool
}),
saleApi.getSaleGoodsList(Object.assign(params, {productPool: result[1].data[0].product_pool}))
saleApi.getSaleGoodsList(Object.assign(_.update(params, 'channel', null), {
productPool: result[1].data[0].product_pool
}))
]).then(subResult => {
// 处理分页等筛选信息
... ...
... ... @@ -9,6 +9,12 @@
const _ = require('lodash');
const helpers = global.yoho.helpers;
const queryString = require('querystring');
const indexUrl = {
boys: helpers.urlFormat('/'),
girls: helpers.urlFormat('/woman'),
kids: helpers.urlFormat('/kids'),
lifestyle: helpers.urlFormat('/lifestyle')
};
// 打折、新品、限量
const checksName = {
... ... @@ -93,7 +99,7 @@ const handleBrandCheckedData = (params, origin) => {
if (!_.isEmpty(origin)) {
_.forEach(origin, (value) => {
if (typeof _.find(intBrands, o => {
return _.isEqual(o, value.id);
return _.isEqual(o, +value.id);
}) !== 'undefined') {
let checked = {
name: value.name
... ... @@ -198,13 +204,11 @@ const formatterFilterBrands = (source, paramBrand, params) => {
index: '0-9',
name: '0~9'
}],
selectedBrands: [],
showMore: true,
showMulti: true
selectedBrands: []
};
// 品牌索引数据处理
// 品牌索引数据处理
for (let i = 65; i < 91; i++) {
dbrand.brandIndex.push({
index: String.fromCharCode(i).toLowerCase(),
... ... @@ -212,8 +216,8 @@ const formatterFilterBrands = (source, paramBrand, params) => {
});
}
// 品牌数据处理
// 分拆品牌参数
// 品牌数据处理
// 分拆品牌参数
let brands = _.split(params.brand, ',');
let intBrands = [];
... ... @@ -227,7 +231,7 @@ const formatterFilterBrands = (source, paramBrand, params) => {
_.forEach(source, function(value) {
let brand = {
checked: (typeof _.find(intBrands, o => {
return _.isEqual(o, value.id);
return _.isEqual(o, +value.id);
}) !== 'undefined'),
href: handleFilterUrl(params, {brand: value.id}),
name: value.brand_name,
... ... @@ -251,6 +255,13 @@ const formatterFilterBrands = (source, paramBrand, params) => {
count++;
});
if (dbrand.default.length > 9) {
Object.assign(dbrand, {
showMore: true,
showMulti: true
});
}
if (paramBrand) {
_.forEach(paramBrand, value => {
let brand = {
... ... @@ -264,11 +275,11 @@ const formatterFilterBrands = (source, paramBrand, params) => {
});
}
// 没有品牌的情况下将 brand 设置为 false,前端不显示 品牌
// 没有品牌的情况下将 brand 设置为 false,前端不显示 品牌
if (_.size(dbrand.brandsShow) <= 0) {
return false;
} else {
// 品牌排序
// 品牌排序
dbrand.brandsShow = _.sortBy(dbrand.brandsShow, o => {
return o.index;
});
... ... @@ -532,7 +543,10 @@ exports.setShopSort = (data, params) => {
});
if (list.length) {
_.set(resData, 'goodsMenu.menuList', list);
resData.goodsMenu = {
menuList: list,
url: `/product/shoplist?navBar=1&shopId=${params.shopId}`
};
}
}
... ... @@ -548,19 +562,6 @@ exports.setShopSort = (data, params) => {
exports.handleFilterData = (origin, params, total) => {
let dest = {
ageLevel: [],
brand: {
default: [],
brandsShow: [],
brandIndex: [{
index: 'all',
name: '全部'
}, {
index: '0-9',
name: '0~9'
}],
showMore: true,
showMulti: true
},
price: [],
gender: [],
color: [],
... ... @@ -867,11 +868,12 @@ exports.handleSeniorFilterData = (data, params) => {
};
exports.handleFilterDataAll = (data, qs) => {
let destFilter = {};
let params = _.cloneDeep(qs);
_.unset(params, 'page'); // 去除筛选项page
let baseFilter = this.handleFilterData(data.filter, params, data.total);
let baseFilter = this.handleFilterData(_.get(data, 'filter', {}), params, data.total);
let seniorFilter = this.handleSeniorFilterData({
style: _.get(data, 'filter.style', []),
standard: _.get(data, 'standard', [])
... ... @@ -880,12 +882,9 @@ exports.handleFilterDataAll = (data, qs) => {
let conditions = _.union(_.get(baseFilter, 'checkedConditions.conditions'),
_.get(seniorFilter, 'checkedConditions.conditions'));
let destFilter = Object.assign({}, seniorFilter, baseFilter);
Object.assign(destFilter, seniorFilter, baseFilter);
if (!destFilter.checkedConditions) {
destFilter.checkedConditions = {};
}
destFilter.checkedConditions.conditions = conditions;
_.set(destFilter, 'checkedConditions.conditions', conditions);
return destFilter;
};
... ... @@ -903,7 +902,7 @@ exports.handlePathNavData = (data, params, page, channel) => {
}
let pathNav = [{
href: '/',
href: indexUrl[channel],
name: rootName, // '首页', // TODO 从根据cookie获取频道
pathTitle: rootName // '首页'
}];
... ... @@ -1131,7 +1130,7 @@ exports.handleSortIntro = (data) => {
_.forEach(data.keyword, function(value) {
sortIntro.keyEntry.push({
name: value.word,
url: value.url
url: value.url.replace(/(parameter_)(\d+)=(\d+)/, (match, p1, p2, p3) => `standard=${p2}_${p3}`)
});
});
... ... @@ -1258,7 +1257,7 @@ exports.handleNextPage = (params, total) => {
let href;
let currentPage = parseInt((_.isEmpty(params.page) ? 1 : params.page), 10); // 当前页
let perPageCount = parseInt((_.isEmpty(params.limit) ? 60 : params.limit), 10); // 每页商品数
let perPageCount = parseInt((_.isEmpty(params.limit) ? 60 : params.limit) - 1, 10); // 每页商品数
let totalPage = parseInt(total / perPageCount, 10) + 1; // 总页数
if (currentPage === totalPage) {
... ... @@ -1270,7 +1269,7 @@ exports.handleNextPage = (params, total) => {
return {
href: href,
src: '//img10.static.yhbimg.com/product/2014/01/15/11/01fa01614784f6239760f1b749663016f1.jpg?' +
'imageMogr2/thumbnail/235x314/extent/235x314/background/d2hpdGU=/position/center/quality/90'
'imageMogr2/thumbnail/235x314/extent/235x314/background/d2hpdGU=/position/center/quality/90'
};
};
... ... @@ -1393,12 +1392,12 @@ exports.getBrandSeo = (channel, brandInfo) => {
bbc = b + bcn + c,
title = bb || b || '',
keywords = [b ? b + ',' : '',
((b && bcn) ? (bb + ',') : ''),
bc || '',
'品牌'].join(''),
((b && bcn) ? (bb + ',') : ''),
bc || '',
'品牌'].join(''),
desc = [b + '正品网购。',
bbc || '',
'品牌官方授权!YOHO! 有货中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。'].join('');
bbc || '',
'品牌官方授权!YOHO! 有货中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。'].join('');
title += (title ? (title + '|') : '') + c + '品牌|YOHO!BUY 有货 100%正品保证';
... ...
... ... @@ -11,6 +11,7 @@ const headerModel = require('../../../doraemon/models/header');
const productProcess = require(`${utils}/product-process`);
const searchHandler = require('./search-handler');
const _ = require('lodash');
const needParams = ['query', 'msort', 'misort'];
/**
* 获取搜索数据
... ... @@ -46,9 +47,12 @@ exports.getSearchData = (params, channel) => {
if (result[1].code === 200) {
let dps = {};
if (params.query) {
dps.query = params.query;
}
_.forEach(needParams, (value) => {
if (params[value]) {
dps[value] = params[value];
}
});
finalResult.search = {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
};
... ... @@ -104,6 +108,11 @@ exports.getBrands4Filter = (params) => {
nparams.price = nmp.join(',');
}
// 生成路径包含callback 导致多次调用多个 callback问题
if (params && params.callback) {
delete params.callback;
}
return searchApi.getBrands4Filter(Object.assign({}, params, nparams)).then(result => {
let dest = [];
... ...
... ... @@ -5,6 +5,8 @@
'use strict';
const _ = require('lodash');
const Fn = require('lodash/fp');
const qs = require('queryString');
const helpers = global.yoho.helpers;
... ... @@ -68,32 +70,34 @@ const hotProducts = (data) => {
*/
const goodsTabBar = (data, shopId) => {
let dest = {
hot: [],
new: []
},
more = {name: 'MORE', href: shopListUrl + '?shopId=' + shopId};
hot: [],
new: []
};
_.forEach(data.hot, (value) => {
if (value.url) {
_.forEach(_.sortBy(data.hot, o => {
return -o.position;
}), (value) => {
if (value.url && value.position) {
dest.hot.push({
name: value.name,
href: value.url
url: value.url
});
}
});
_.forEach(data.new, (value) => {
if (value.url) {
_.forEach(_.sortBy(data.new, o => {
return -o.position;
}), (value) => {
if (value.url && value.position) {
dest.new.push({
name: value.name,
href: value.url
url: value.url
});
}
});
dest.hot.push(more);
dest.new.push(more);
dest.hot.push({name: 'MORE', url: `${shopListUrl}?navBar=2&order=s_n_desc&shopId=${shopId}`});
dest.new.push({name: 'MORE', url: `${shopListUrl}?navBar=3&order=s_t_desc&shopId=${shopId}`});
return dest;
};
... ... @@ -144,17 +148,24 @@ const navigationBar = (data, shopId) => {
},
{
name: '人气单品',
url: `${shopListUrl}/?navBar=2&shopId=${shopId}`
url: `${shopListUrl}/?navBar=2&order=s_n_desc&shopId=${shopId}`
},
{
name: '新品上架',
url: `${shopListUrl}/?navBar=3&shopId=${shopId}`
url: `${shopListUrl}/?navBar=3&order=s_t_desc&shopId=${shopId}`
}
];
return {navigationBar: _.union(shopNav, _.filter(data, (value) => {
return value.url;
}))};
_.forEach(data, (value) => {
if (value.url) {
shopNav.push({
name: value.name,
url: `${value.url}&navBar=${shopNav.length}`
});
}
});
return {navigationBar: shopNav};
};
/**
... ... @@ -166,9 +177,10 @@ const largeSlideImg = (data) => {
let dest = [];
_.forEach(data, (value) => {
value = _.get(value, 'data[0]', {});
dest.push({
img: value.data[0].src,
url: helpers.urlFormat(value.data[0].url)
img: value.src,
url: value.url
});
});
... ... @@ -184,9 +196,10 @@ const oneRowTwoColImages = (data) => {
let dest = [];
_.forEach(data, (value) => {
value = _.get(value, 'data[0]', {});
dest.push({
img: value.data[0].src,
url: helpers.urlFormat(value.data[0].url)
img: value.src,
url: value.url
});
});
return {oneRowTwoColImages: dest};
... ... @@ -206,7 +219,7 @@ const recommend = (data) => {
name: value.name,
img: value.src,
title: value.title,
url: helpers.urlFormat(value.url)
url: value.url
});
});
... ... @@ -277,62 +290,130 @@ const hotRecommend = (data) => {
};
/**
* 店铺装修楼层数据
* @param data 装修数据
* @returns {{}}
* 水牌
*/
exports.getShopDecorator = (data, params, shopId) => {
let dest = {
newArrivel: {},
hotSingle: {}
const signboard = (data) => {
let list = [];
_.forEach(data, value => {
if (value.data) {
_.forEach(value.data, val => {
list.push({
img: helpers.image(val.src, 160, 240),
url: val.url
});
});
}
});
return {
title: _.get(list, '[0].title', ''),
list: list
};
};
_.forEach(data.list, (value) => {
let info = JSON.parse(value.resource_data);
let tabBar;
switch (value.resource_name) {
case 'signboard':
break;
case 'newProducts':
Object.assign(dest.newArrivel, newProducts(info));
break;
case 'hotProducts':
Object.assign(dest.hotSingle, hotProducts(info));
break;
case 'goodsTabBar':
tabBar = goodsTabBar(info);
Object.assign(dest.newArrivel, {navs: tabBar.new});
Object.assign(dest.hotSingle, {navs: tabBar.hot});
break;
case 'shopTopBanner':
Object.assign(dest, shopTopBanner(info));
break;
case 'shopTopBanner_base':
Object.assign(dest, shopTopBannerBase(info));
break;
case 'navigationBar':
Object.assign(dest, navigationBar(info, shopId));
break;
case 'largeSlideImg':
Object.assign(dest, largeSlideImg(info));
break;
case 'oneRowTwoColImages':
Object.assign(dest, oneRowTwoColImages(info, shopId));
break;
case 'recommend':
Object.assign(dest, recommend(info, shopId));
break;
case 'brandBrowse':
Object.assign(dest, brandBrowse(info, params));
break;
case 'hotRecommend':
Object.assign(dest, hotRecommend(info));
break;
default:
break;
// 销售类目
const _handleSaleCategory = (shopId, resourceObj) => {
const thisShop = (categoryId) => shopListUrl + '?' + qs.stringify({
productPool: categoryId,
shopId: shopId,
navBar: -1
});
let hasSaleCategory = Fn.pipe(Fn.prop('linkType'), Fn.eq('1'));
if (hasSaleCategory(resourceObj)) {
return Object.assign(resourceObj, {url: thisShop(resourceObj.categoryId)});
}
_(resourceObj).forEach((value) => {
if (_.has(value, 'data')) {
_.forEach(value.data, (it) => {
if (hasSaleCategory(it)) {
Object.assign(it, {url: thisShop(it.categoryId)});
}
});
}
if (hasSaleCategory(value)) {
Object.assign(value, {url: thisShop(value.categoryId)});
}
});
return resourceObj;
};
/**
* 店铺装修楼层数据
* @param data 装修数据
* @returns {{}}
*/
exports.getShopDecorator = (data, params, shopId, base) => {
let dest = {};
if (base) {
_.forEach(data.list, (value) => {
let info = Fn.pipe(JSON.parse, _.partial(_handleSaleCategory, shopId))(value.resource_data);
switch (value.resource_name) {
case 'signboard':
dest.signboard = signboard(info);
break;
case 'shopTopBanner_base':
Object.assign(dest, shopTopBannerBase(info));
break;
default:
break;
}
});
} else {
Object.assign(dest, {
newArrivel: {},
hotSingle: {}
});
_.forEach(data.list, (value) => {
let info = Fn.pipe(JSON.parse, _.partial(_handleSaleCategory, shopId))(value.resource_data);
let tabBar;
switch (value.resource_name) {
case 'newProducts':
Object.assign(dest.newArrivel, newProducts(info));
break;
case 'hotProducts':
Object.assign(dest.hotSingle, hotProducts(info));
break;
case 'goodsTabBar':
tabBar = goodsTabBar(info, shopId);
Object.assign(dest.newArrivel, {navs: tabBar.new});
Object.assign(dest.hotSingle, {navs: tabBar.hot});
break;
case 'shopTopBanner':
Object.assign(dest, shopTopBanner(info));
break;
case 'navigationBar':
Object.assign(dest, navigationBar(info, shopId));
break;
case 'largeSlideImg':
Object.assign(dest, largeSlideImg(info, shopId));
break;
case 'oneRowTwoColImages':
Object.assign(dest, oneRowTwoColImages(info, shopId));
break;
case 'recommend':
Object.assign(dest, recommend(info, shopId));
break;
case 'brandBrowse':
Object.assign(dest, brandBrowse(info, params));
break;
case 'hotRecommend':
Object.assign(dest, hotRecommend(info));
break;
default:
break;
}
});
}
return dest;
};
... ...
{{> list/shop-list}}
\ No newline at end of file
... ...
... ... @@ -77,9 +77,9 @@
{{#for_stu}}<span class="stu-tag"> 学生价 </span>{{/for_stu}}
</p>
<div class="hideList hide">
{{#goodsList}}
<li data-src="{{image images_url 280 374}}" data-url="{{../url}}"></li>
{{/goodsList}}
{{#goods_list}}
<li data-src="{{image images_url 280 374}}" data-url="{{../url}}" data-status="{{status}}"></li>
{{/goods_list}}
</div>
</div>
</div>
... ...
{
"name": "yohobuy-node",
"version": "5.0.0",
"version": "5.0.2",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ...
... ... @@ -104,7 +104,10 @@ exports.init = function(num) {
event.target.find('.hideList > li').each(function() {
pic = $(this).data();
pic.coverImg = pic.src;
pics.push(pic);
if (+pic.status) { // 过滤下架商品
pics.push(pic);
}
});
colorList = createColorList(pics, _from);
... ...
... ... @@ -4,7 +4,8 @@ const fp = require('lodash/fp');
const camelCase = global.yoho.camelCase;
const helpers = global.yoho.helpers;
const images = require(`${global.utils}/images`);
// const images = require(`${global.utils}/images`);
// NOTE: 这里修改了图片质量的参数
helpers.image = _.flow(helpers.image, fp.replace(/\/quality\/\d*$/, '/quality/90'));
... ... @@ -393,7 +394,7 @@ exports.processProduct = (productData, options) => {
result.id = productData.product_skn;
result.product_id = productData.product_id;
result.thumb = images.getImageUrl(productData.default_images, options.width, options.height);
result.thumb = helpers.image(productData.default_images, options.width, options.height);
result.name = productData.product_name;
result.price = !productData.market_price ? false : productData.market_price;
result.salePrice = productData.sales_price;
... ...