Authored by 郭成尧

sub-router

... ... @@ -25,6 +25,9 @@ const MemcachedStore = memcached(session);
app.locals.devEnv = app.get('env') === 'development';
app.locals.version = pkg.version;
// 访问域名层级
app.set('subdomain offset', 3);
// 全局注册library
yohoLib.global(config);
... ... @@ -78,17 +81,20 @@ app.use((req, res, next) => {
// dispatcher
try {
const shopCheck = require('./doraemon/middleware/shop-check');
const user = require('./doraemon/middleware/user');
const subDomain = require('./doraemon/middleware/sub-domain');
const setYohoData = require('./doraemon/middleware/set-yoho-data');
const errorHanlder = require('./doraemon/middleware/error-handler');
const setPageInfo = require('./doraemon/middleware/set-pageinfo');
const devtools = require('./doraemon/middleware/devtools');
const seo = require('./doraemon/middleware/seo');
// YOHO 前置中间件
app.use(shopCheck());
app.use(subDomain());
app.use(setYohoData());
app.use(user());
app.use(seo());
app.use(setPageInfo());
if (app.locals.devEnv) {
... ...
/**
* 分类页面
*/
'use strict';
const cateModel = require('../models/cate');
const headerModel = require('../../../doraemon/models/header');
const helpers = global.yoho.helpers;
let index = (req, res, next) => {
cateModel.getCateData(req.yoho.channel).then((result) => {
res.render('cate', {
module: 'channel',
page: 'cate',
title: '商品分类',
pageHeader: headerModel.setNav({
navTitle: '商品分类'
}),
pageFooter: false,
category: {
nav: result.nav,
list: result.list,
searchUrl: helpers.urlFormat('/search', null, 'search')
}
});
}).catch(next);
};
module.exports = {
index
};
... ...
/**
* 分类页面 model
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/07/06
*/
'use strict';
const _ = require('lodash');
const api = global.yoho.API;
const helpers = global.yoho.helpers;
const camelCase = global.yoho.camelCase;
const genderMap = {
boys: '1,3',
girls: '2,3',
kids: '1,2,3',
lifestyle: '1,2,3'
};
let _processCateData = (list, channel) => {
let nav = ['boys', 'girls', 'kids', 'lifestyle'];
nav = _.map(nav, function(item) {
return {
name: _.capitalize(item),
channel: item,
focus: item === channel
};
});
list = camelCase(list);
_.map(list, function(item, key) {
item.focus = key === channel;
_.map(item, function(firstItem) {
// 如果有二级菜单,二级菜单跳转,否则一级菜单跳转
if (firstItem.sub && firstItem.sub.length) {
_.map(firstItem.sub, function(secondItem) {
secondItem.url = helpers.urlFormat('/', {
sort: _.get(secondItem, 'relationParameter.sort'),
sort_name: secondItem.categoryName,
gender: genderMap[channel] || ''
}, 'list');
});
firstItem.sub.unshift({
categoryName: `全部${firstItem.categoryName}`,
url: helpers.urlFormat('/', {
sort: _.get(firstItem, 'relationParameter.sort'),
sort_name: firstItem.categoryName,
gender: genderMap[channel] || ''
}, 'list')
});
} else {
firstItem.url = helpers.urlFormat('/', {
sort: _.get(firstItem, 'relationParameter.sort'),
sort_name: firstItem.categoryName,
gender: genderMap[channel] || ''
}, 'list');
}
});
});
return {
nav,
list
};
};
let getCateData = (channel) => {
return api.get('', {
method: 'app.sort.get'
}, {
cache: true
}).then((result) => {
if (!result.code || result.code !== 200 || !result.data) {
return [];
}
// 统一频道名称
result.data.boys = result.data.boy;
result.data.girls = result.data.girl;
_.unset(result.data, 'boy');
_.unset(result.data, 'girl');
return result.data;
}).then((list) => {
return _processCateData(list, channel);
});
};
module.exports = {
getCateData
};
... ...
... ... @@ -9,6 +9,7 @@
const express = require('express');
const cRoot = './controllers';
const channel = require(cRoot);
const cate = require(cRoot + '/cate');
const router = express.Router(); // eslint-disable-line
... ... @@ -20,4 +21,6 @@ router.get('/lifestyle', channel.switchChannel, channel.lifestyle);
router.get('/channel/bottomBanner', channel.bottomBanner);
router.get('/cate', cate.index);
module.exports = router;
... ...
<div class="category-page yoho-page">
{{# category}}
<div id="search-input" class="search-input">
<a href={{searchUrl}}>
<i class="search-icon iconfont">&#xe60f;</i>
<p>搜索商品</p>
</a>
</div>
<ul class="category-nav clearfix">
{{# nav}}
<li class="{{#if focus}}focus{{/if}}" data-channel="{{channel}}">
<span>{{name}}</span>
</li>
{{/ nav}}
</ul>
<div class="category-container clearfix">
{{#each list}}
<div class="content {{@key}} {{#unless focus}}hide{{/unless}}">
<ul class="primary-level">
{{# this}}
<li class="p-level-item{{#if @first}} focus{{/if}}">
{{#if url}}
<a href={{url}}> {{categoryName}}</a>
{{^}}
{{categoryName}}
{{/if}}
</li>
{{/ this}}
</ul>
<div class="sub-level-container">
{{# this}}
<ul class="sub-level {{#unless @first}}hide{{/unless}}">
{{# sub}}
<li>
<a href={{url}}>
{{categoryName}}
</a>
</li>
{{/ sub}}
</ul>
{{/ this}}
</div>
</div>
{{/each}}
</div>
{{/ category}}
</div>
... ...
... ... @@ -112,14 +112,20 @@ exports.intro = (req, res, next) => {
return next();
}
introModel.getintroData({
introModel.getintroIntro({
productskn: req.params.productskn
}, req).then((result) => {
res.render('detail/intro', {
result: result,
layout: false
});
}).catch(next);
}, req).then(html => {
res.send(html);
});
// introModel.getintroData({
// productskn: req.params.productskn
// }, req).then((result) => {
// res.render('detail/intro', {
// result: result,
// layout: false
// });
// }).catch(next);
};
/**
... ...
... ... @@ -94,7 +94,7 @@ const getConsults = (id, page, limit) => {
*/
let _processComment = (data) => {
let result = {
commentsNum: data.pageResponse.totalCount,
commentsNum: (data.pageResponse && data.pageResponse.totalCount) || 0,
comments: []
};
... ...
... ... @@ -138,6 +138,426 @@ const _getLimitCodeUrl = (productCode, skn, ua) => {
* @param origin Object 原始数据
* @return dest Object 格式化数据
*/
// 老接口h5的代码
// const _detailDataPkg = (origin, uid, vipLevel, ua) => {
// let dest = {}, // 结果输出
// thumbImageList = [],
// colorGroup = {},
// sizeGroup = [],
// totalStorageNum = 0;
// // 商品名称
// if (origin.productName === null || typeof origin.productName === 'undefined') {
// return dest;
// }
// dest.goodsName = origin.productName;
// // 是否是虚拟商品
// dest.virtualGoods = origin.attribute * 1 === 3 ? true : false;
// // 用户未登录时
// if (!uid) {
// let params = {};
// params.refer = helpers.urlFormat('/product/show_' + origin.erpProductId + '.html');
// dest.loginUrl = helpers.urlFormat('/signin.html', params);
// }
// // 商品促销短语
// if (origin.salesPhrase) {
// dest.goodsSubtitle = origin.salesPhrase;
// }
// // 促销信息 TODO: 换新接口
// if (origin.promotionBoList) {
// let discountList = [];
// _.forEach(origin.promotionBoList, function(value) {
// discountList.push({
// text: `【${value.promotionType}】${value.promotionTitle}`
// });
// });
// if (discountList.length) {
// dest.goodsDiscount = {
// list: discountList
// };
// }
// }
// // 商品标签
// if (origin.productTagBoList) {
// let productTags = {};
// _.forEach(origin.productTagBoList, function(value) {
// switch (value.tagLabel) {
// case 'is_soon_sold_out':
// productTags.is_soon_sold_out = true;
// break;
// case 'is_new':
// productTags.is_new = true;
// break;
// case 'is_discount':
// productTags.is_discount = true;
// break;
// case 'is_limited':
// productTags.is_limited = true;
// break;
// case 'is_yohood':
// productTags.is_yohood = true;
// break;
// case 'is_advance':
// productTags.is_advance = true;
// break;
// default:
// break;
// }
// });
// dest.tags = productTags;
// }
// // 商品价格
// if (origin.productPriceBo) {
// let originPrice = origin.productPriceBo;
// let goodsPrice = {
// currentPrice: originPrice.formatSalesPrice
// };
// if (originPrice.formatMarketPrice !== originPrice.formatSalesPrice) {
// goodsPrice.previousPrice = originPrice.formatMarketPrice;
// }
// dest.goodsPrice = goodsPrice;
// // 商品返回 有货币
// if (originPrice.yohoCoinNum) {
// dest.commodityReturn = originPrice.yohoCoinNum;
// }
// }
// // VIP 商品价格
// if (origin.productPriceBo.vipPrices) {
// let vipList = [];
// _.forEach(origin.productPriceBo.vipPrices, function(value) {
// vipList.push({
// level: value.vipLevel,
// text: value.vipPrice,
// currentLevel: (value.vipLevel === vipLevel)
// });
// });
// if (vipList.length) {
// dest.vipLevel = {
// list: vipList
// };
// }
// }
// // 上市期
// if (origin.expectArrivalTime) {
// dest.periodOfMarket = `${origin.expectArrivalTime}月`;
// }
// // 商品咨询
// dest.feedbacks = {
// consults: [],
// consultsNum: 0
// };
// let consultParams = {
// product_id: origin.id
// };
// if (_.has(dest, 'feedbacks.consultsNum')) {
// consultParams.total = dest.feedbacks.consultsNum;
// dest.feedbacks.consultsUrl = helpers.urlFormat('/product/detail/consults', consultParams);
// } else {
// dest.feedbacks.consultsUrl = helpers.urlFormat('/product/detail/consultform', consultParams);
// }
// // 商品评价
// dest.feedbacks.commentsNum = 0;
// if (origin.commentBoWrapper) {
// dest.feedbacks.commentsNum = origin.commentBoWrapper.commentTotal;
// let commentList = [];
// _.forEach(origin.commentBoWrapper.commentBoList, function(value) {
// commentList.push({
// userName: value.nickName,
// desc: `${value.colorName}/${value.sizeName}`,
// content: value.content ? value.content : '',
// time: value.createTime
// });
// });
// dest.feedbacks.comments = commentList;
// dest.feedbacks.commentsUrl = helpers.urlFormat('/product/detail/comments', {
// product_id: origin.id,
// total: dest.feedbacks.commentsNum
// });
// }
// // 品牌信息
// if (origin.brand) {
// let extra = `?productSkn=${origin.erpProductId}&brandId=${origin.brand.id}`;
// dest.preferenceUrl = `/product/detail/preference${extra}`;
// }
// dest.productSkn = origin.erpProductId;
// // 商品信息
// if (origin.goodsList) {
// let goodsGroup = [],
// sizeName = '',
// colorList = [],
// sizeList = {},
// allSizeList = {},
// colorStorageGroup = {},
// colorStorageNum = 0;
// _.forEach(origin.goodsList, function(value) {
// if (value.status === 0) {
// return;
// }
// colorStorageNum = 0;
// // 商品分组
// if (value.goodsImagesList) {
// _.forEach(value.goodsImagesList, function(good) {
// goodsGroup.push({
// goodsId: good.goodsId,
// img: good.imageUrl
// });
// });
// }
// // 商品的尺码列表
// colorStorageGroup[value.productSkc] = {};
// if (value.goodsSizeBoList) {
// sizeList[value.productSkc] = [];
// _.forEach(value.goodsSizeBoList, function(size) {
// sizeList[value.productSkc].push({
// id: size.id,
// skuId: size.goodsSizeSkuId,
// goodsId: size.goodsId,
// colorId: size.colorId,
// name: size.sizeName,
// sizeNum: size.goodsSizeStorageNum
// });
// sizeName = size.sizeName;
// // 所有尺码列表,赋值用于前端展示默认尺码的时候
// // 判断出没有库存则显示灰色
// let build = {
// id: size.id,
// storage: size.goodsSizeStorageNum
// };
// allSizeList[sizeName] = (allSizeList[sizeName] === null ||
// typeof allSizeList[sizeName] === 'undefined') ? build :
// allSizeList[sizeName];
// colorStorageNum += parseInt(size.goodsSizeStorageNum, 10);
// colorStorageGroup[value.productSkc][sizeName] = parseInt(size.goodsSizeStorageNum, 10);
// });
// // 颜色分组
// colorList.push({
// id: value.colorId,
// skcId: value.productSkc,
// name: value.colorName,
// goodsName: value.goodsName,
// colorNum: colorStorageNum
// });
// }
// // 缩略图
// thumbImageList.push({
// img: value.colorImage
// });
// // 商品库存总数
// totalStorageNum += _.toNumber(colorStorageNum);
// });
// // 遍历所有尺码,构建颜色显示数据
// let i = 1;
// sizeGroup[0] = {
// size: []
// };
// _.forEach(allSizeList, (value, key) => {
// // 默认尺码
// sizeGroup[0].size.push({
// name: key,
// sizeNum: _.toNumber(value.storage) > 0 ? true : false,
// id: value.id
// });
// colorGroup[i] = {
// color: []
// };
// // 各个颜色的尺码, 每行显示一个尺码对应的颜色
// _.forEach(colorList, (colorArr) => {
// let tempColorArr = _.cloneDeep(colorArr);
// if (colorStorageGroup[tempColorArr.skcId] &&
// colorStorageGroup[tempColorArr.skcId][key]) {
// tempColorArr.colorNum = colorStorageGroup[tempColorArr.skcId][key];
// } else {
// tempColorArr.colorNum = 0;
// }
// colorGroup[i].color.push(Object.assign({}, tempColorArr));
// });
// colorGroup[i].id = value.id;
// ++i;
// });
// colorGroup[0] = {
// color: []
// };
// // 遍历所有颜色, 构建尺码显示数据
// i = 1;
// _.forEach(colorList, function(value) {
// // 各个尺码的颜色, 每行显示一个颜色的对应尺码
// sizeGroup[i] = {
// size: sizeList[value.skcId],
// colorId: value.skcId
// };
// // 默认颜色
// colorGroup[0].color.push(value);
// ++i;
// });
// // 商品图:多个
// if (goodsGroup.length > 1) {
// let bannerList = [];
// _.forEach(goodsGroup, function(value) {
// bannerList.push({
// img: value.img
// });
// });
// dest.bannerTop = {
// list: bannerList
// };
// } else if (goodsGroup[0] !== null && typeof goodsGroup[0] !== 'undefined') {
// dest.bannerTop = {
// img: goodsGroup[0].img
// };
// }
// }
// // 悬浮的购物车信息
// dest.cartInfo = {
// cartUrl: helpers.urlFormat('/cart/index/index'),
// numInCart: 0,
// goodsInstore: origin.storage
// };
// let soldOut = (origin.storage === 0) || (origin.status === 0 || totalStorageNum === 0);
// let notForSale = origin.attribute === 2;
// // 显示加入购物车链接
// if (!soldOut && !notForSale) {
// _.orderBy(colorGroup);
// Object.assign(dest.cartInfo, {
// productId: origin.id,
// thumbs: thumbImageList,
// name: dest.goodsName ? dest.goodsName : '',
// price: dest.goodsPrice.previousPrice ? dest.goodsPrice.previousPrice : '',
// salePrice: dest.goodsPrice.currentPrice ? dest.goodsPrice.currentPrice : '',
// totalNum: totalStorageNum,
// colors: _.toArray(colorGroup),
// sizes: sizeGroup
// });
// // 限购商品
// if (origin.isLimitBuy === 'Y') {
// // 是否开售
// let isBeginSale = (origin.saleStatus !== null && typeof origin.saleStatus !== 'undefined' &&
// origin.saleStatus === 1);
// // 限购商品有关的展示状态
// let showStatus = 1;
// if (origin.showStatus !== null && typeof origin.showStatus !== 'undefined') {
// showStatus = parseInt(origin.showStatus, 10);
// }
// // 处理限购商品有关的按钮状态
// dest = _procShowStatus(dest, showStatus, isBeginSale);
// dest.cartInfo.limitProductCode = origin.limitProductCode;
// dest.cartInfo.limitCodeUrl = _getLimitCodeUrl(origin.limitProductCode, origin.erpProductId, ua);
// dest.cartInfo.limitProductPay = helpers.urlFormat('/cart/index/orderEnsure');
// } else {
// dest.cartInfo.addToCartUrl = helpers.urlFormat('/product/buy_' + origin.id + '_' +
// origin.goodsList.id + '.html');
// }
// } else if (notForSale) {
// dest.cartInfo.notForSale = true;
// } else if (soldOut) {
// dest.cartInfo.soldOut = true;
// }
// // 是否收藏
// dest.isCollect = false;
// if (origin.isCollect !== null && typeof origin.isCollect !== 'undefined' && origin.isCollect === 'Y') {
// dest.isCollect = true;
// dest.cartInfo.isCollect = true;
// }
// // 底部简介URL链接
// dest.introUrl = '/product/detail/intro/' + origin.erpProductId;
// dest.id = origin.id;
// // 虚拟商品(门票)
// if (origin.attribute * 1 === 3) {
// dest.tickets = true;
// dest.ticketsConfirm = helpers.urlFormat('/cart/index/ticketsConfirm');
// // 展览票
// if (origin.erpProductId * 1 === SINGLE_TICKETS_SKN) {
// dest.single = true;
// } else {
// // 套票
// dest.package = true;
// }
// // 购票限制
// dest.cartInfo.limit = 4;
// // 清空活动
// dest.goodsDiscount = [];
// // 来自登录页,自动弹出选择框
// // if (isset($_GET['product_type']) && $_GET['product_type'] == 'ticket') {
// // $result['showPannel'] = true;
// // }
// }
// return dest;
// };
const _detailDataPkg = (origin, uid, vipLevel, ua) => {
let dest = {}, // 结果输出
thumbImageList = [],
... ... @@ -145,28 +565,26 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
sizeGroup = [],
totalStorageNum = 0;
// 商品名称
if (origin.productName === null || typeof origin.productName === 'undefined') {
// 商品名称
if (!origin.productName) {
dest.feedbacks = {};
return dest;
}
dest.goodsName = origin.productName;
// 是否是虚拟商品
dest.virtualGoods = origin.attribute * 1 === 3 ? true : false;
// 是否是虚拟商品
dest.virtualGoods = (origin.attribute * 1 === 3);
// 用户未登录时
if (!uid) {
let params = {};
params.refer = helpers.urlFormat('/product/show_' + origin.erpProductId + '.html');
dest.loginUrl = helpers.urlFormat('/signin.html', params);
dest.loginUrl = helpers.urlFormat('/signin.html', {
refer: helpers.urlFormat('/product/show_' + origin.productSkn + '.html')
});
}
// 商品促销短语
if (origin.salesPhrase) {
dest.goodsSubtitle = origin.salesPhrase;
}
// 商品促销短语
origin.salesPhrase && (dest.goodsSubtitle = origin.salesPhrase);
// 促销信息 TODO: 换新接口
if (origin.promotionBoList) {
... ... @@ -178,94 +596,61 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
});
});
if (discountList.length) {
dest.goodsDiscount = {
list: discountList
};
}
discountList.length && (dest.goodsDiscount = {
list: discountList
});
}
// 商品标签
if (origin.productTagBoList) {
// 商品标签
if (origin.tags) {
let productTags = {};
_.forEach(origin.productTagBoList, function(value) {
switch (value.tagLabel) {
case 'is_soon_sold_out':
productTags.is_soon_sold_out = true;
break;
case 'is_new':
productTags.is_new = true;
break;
case 'is_discount':
productTags.is_discount = true;
break;
case 'is_limited':
productTags.is_limited = true;
break;
case 'is_yohood':
productTags.is_yohood = true;
break;
case 'is_advance':
productTags.is_advance = true;
break;
default:
break;
}
_.forEach(origin.tags, function(value) {
productTags[value] = true;
});
dest.tags = productTags;
}
// 商品价格
if (origin.productPriceBo) {
let originPrice = origin.productPriceBo;
let goodsPrice = {
currentPrice: originPrice.formatSalesPrice,
studentPrice: originPrice.studentPrice
};
if (originPrice.formatMarketPrice !== originPrice.formatSalesPrice) {
goodsPrice.previousPrice = originPrice.formatMarketPrice;
}
dest.goodsPrice = goodsPrice;
// 商品价格
let goodsPrice = {
currentPrice: origin.formatSalesPrice === '0' ? origin.formatMarketPrice : origin.formatSalesPrice
};
// 商品返回 有货币
if (origin.formatSalesPrice !== '0' && origin.formatMarketPrice !== origin.formatSalesPrice) {
goodsPrice.previousPrice = origin.formatMarketPrice;
}
if (originPrice.yohoCoinNum) {
dest.commodityReturn = originPrice.yohoCoinNum;
}
dest.goodsPrice = goodsPrice;
// 学生有货币
if (originPrice.studentCoinNum) {
dest.studentCoinNum = originPrice.studentCoinNum;
}
}
// 商品返回 YOHO 币
origin.yohoCoinNum && (dest.commodityReturn = origin.yohoCoinNum);
// VIP 商品价格
if (origin.productPriceBo.vipPrices) {
// VIP 商品价格
if (origin.vip) {
let vipList = [];
let levelList = {
银卡: 1,
金卡: 2,
白金: 3
};
_.forEach(origin.productPriceBo.vipPrices, function(value) {
_.forEach(origin.vip, function(value) {
vipList.push({
level: value.vipLevel,
text: value.vipPrice,
level: levelList[value.caption],
text: value.price,
currentLevel: (value.vipLevel === vipLevel)
});
});
if (vipList.length) {
dest.vipLevel = {
list: vipList
};
}
vipList.length && (dest.vipLevel = {
list: vipList
});
}
// 上市期
if (origin.expectArrivalTime) {
dest.periodOfMarket = `${origin.expectArrivalTime}月`;
}
// 上市期
origin.expectArrivalTime && (dest.periodOfMarket = `${origin.expectArrivalTime}月`);
// 商品咨询
dest.feedbacks = {
... ... @@ -274,7 +659,7 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
};
let consultParams = {
product_id: origin.id
product_id: origin.productId
};
if (_.has(dest, 'feedbacks.consultsNum')) {
... ... @@ -284,39 +669,19 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
dest.feedbacks.consultsUrl = helpers.urlFormat('/product/detail/consultform', consultParams);
}
// 商品评价
dest.feedbacks.commentsNum = 0;
if (origin.commentBoWrapper) {
dest.feedbacks.commentsNum = origin.commentBoWrapper.commentTotal;
let commentList = [];
_.forEach(origin.commentBoWrapper.commentBoList, function(value) {
commentList.push({
userName: value.nickName,
desc: `${value.colorName}/${value.sizeName}`,
content: value.content ? value.content : '',
time: value.createTime
});
});
dest.feedbacks.comments = commentList;
dest.feedbacks.commentsUrl = helpers.urlFormat('/product/detail/comments', {
product_id: origin.id,
total: dest.feedbacks.commentsNum
});
}
// 商品评价
dest.feedbacks.commentsUrl = helpers.urlFormat('/product/detail/comments', {
product_id: origin.productId,
});
// 品牌信息
if (origin.brand) {
let extra = `?productSkn=${origin.erpProductId}&brandId=${origin.brand.id}`;
let extra = `?productSkn=${origin.productSkn}&brandId=${origin.brandInfo.brandId}`;
dest.preferenceUrl = `/product/detail/preference${extra}`;
}
dest.productSkn = origin.erpProductId;
dest.productSkn = origin.productSkn;
// 商品信息
if (origin.goodsList) {
... ... @@ -335,11 +700,11 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
colorStorageNum = 0;
// 商品分组
if (value.goodsImagesList) {
_.forEach(value.goodsImagesList, function(good) {
// 商品分组
if (value.imagesList) {
_.forEach(value.imagesList, function(good) {
goodsGroup.push({
goodsId: good.goodsId,
goodsId: value.goodsId,
img: good.imageUrl
});
});
... ... @@ -347,16 +712,16 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
// 商品的尺码列表
colorStorageGroup[value.productSkc] = {};
if (value.goodsSizeBoList) {
if (value.sizeList) {
sizeList[value.productSkc] = [];
_.forEach(value.goodsSizeBoList, function(size) {
_.forEach(value.sizeList, function(size) {
sizeList[value.productSkc].push({
id: size.id,
skuId: size.goodsSizeSkuId,
goodsId: size.goodsId,
colorId: size.colorId,
id: size.sizeId,
skuId: size.productSku,
goodsId: value.goodsId,
colorId: value.colorId,
name: size.sizeName,
sizeNum: size.goodsSizeStorageNum
sizeNum: size.storageNumber
});
sizeName = size.sizeName;
... ... @@ -364,16 +729,14 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
// 所有尺码列表,赋值用于前端展示默认尺码的时候
// 判断出没有库存则显示灰色
let build = {
id: size.id,
storage: size.goodsSizeStorageNum
id: size.sizeId,
storage: size.storageNumber
};
allSizeList[sizeName] = (allSizeList[sizeName] === null ||
typeof allSizeList[sizeName] === 'undefined') ? build :
allSizeList[sizeName];
colorStorageNum += parseInt(size.goodsSizeStorageNum, 10);
colorStorageGroup[value.productSkc][sizeName] = parseInt(size.goodsSizeStorageNum, 10);
typeof allSizeList[sizeName] === 'undefined') ? build : allSizeList[sizeName];
colorStorageNum += parseInt(size.storageNumber, 10);
colorStorageGroup[value.productSkc][sizeName] = parseInt(size.storageNumber, 10);
});
// 颜色分组
... ... @@ -381,7 +744,6 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
id: value.colorId,
skcId: value.productSkc,
name: value.colorName,
goodsName: value.goodsName,
colorNum: colorStorageNum
});
}
... ... @@ -401,10 +763,8 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
sizeGroup[0] = {
size: []
};
_.forEach(allSizeList, (value, key) => {
// 默认尺码
// 默认尺码
sizeGroup[0].size.push({
name: key,
sizeNum: _.toNumber(value.storage) > 0 ? true : false,
... ... @@ -464,7 +824,7 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
dest.bannerTop = {
list: bannerList
};
} else if (goodsGroup[0] !== null && typeof goodsGroup[0] !== 'undefined') {
} else if (goodsGroup[0]) {
dest.bannerTop = {
img: goodsGroup[0].img
};
... ... @@ -475,10 +835,9 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
dest.cartInfo = {
cartUrl: helpers.urlFormat('/cart/index/index'),
numInCart: 0,
goodsInstore: origin.storage
goodsInstore: origin.storageSum
};
let soldOut = (origin.storage === 0) || (origin.status === 0 || totalStorageNum === 0);
let soldOut = (origin.storageSum === 0) || (totalStorageNum === 0); // status
let notForSale = origin.attribute === 2;
// 显示加入购物车链接
... ... @@ -486,7 +845,7 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
_.orderBy(colorGroup);
Object.assign(dest.cartInfo, {
productId: origin.id,
productId: origin.productId,
thumbs: thumbImageList,
name: dest.goodsName ? dest.goodsName : '',
price: dest.goodsPrice.previousPrice ? dest.goodsPrice.previousPrice : '',
... ... @@ -496,28 +855,25 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
sizes: sizeGroup
});
// 限购商品
if (origin.isLimitBuy === 'Y') {
// 是否开售
let isBeginSale = (origin.saleStatus !== null && typeof origin.saleStatus !== 'undefined' &&
origin.saleStatus === 1);
// 限购商品
if (origin.isLimitBuy) {
// 是否开售
let isBeginSale = (origin.saleStatus === 1);
// 限购商品有关的展示状态
let showStatus = 1;
if (origin.showStatus !== null && typeof origin.showStatus !== 'undefined') {
showStatus = parseInt(origin.showStatus, 10);
}
origin.showStatus && (showStatus = parseInt(origin.showStatus, 10));
// 处理限购商品有关的按钮状态
dest = _procShowStatus(dest, showStatus, isBeginSale);
dest.cartInfo.limitProductCode = origin.limitProductCode;
dest.cartInfo.limitCodeUrl = _getLimitCodeUrl(origin.limitProductCode, origin.erpProductId, ua);
dest.cartInfo.limitCodeUrl = _getLimitCodeUrl(origin.limitProductCode, origin.productSkn, ua);
dest.cartInfo.limitProductPay = helpers.urlFormat('/cart/index/orderEnsure');
} else {
dest.cartInfo.addToCartUrl = helpers.urlFormat('/product/buy_' + origin.id + '_' +
origin.goodsList.id + '.html');
dest.cartInfo.addToCartUrl = helpers.urlFormat('/product/buy_' + origin.productId + '_' +
origin.goodsList.goodsId + '.html');
}
} else if (notForSale) {
dest.cartInfo.notForSale = true;
... ... @@ -527,22 +883,22 @@ const _detailDataPkg = (origin, uid, vipLevel, ua) => {
// 是否收藏
dest.isCollect = false;
if (origin.isCollect !== null && typeof origin.isCollect !== 'undefined' && origin.isCollect === 'Y') {
if (origin.isCollect === 'Y') {
dest.isCollect = true;
dest.cartInfo.isCollect = true;
}
// 底部简介URL链接
dest.introUrl = '/product/detail/intro/' + origin.erpProductId;
dest.id = origin.id;
// 底部简介URL链接
dest.introUrl = '/product/detail/intro/' + origin.productSkn;
dest.id = origin.productId;
// 虚拟商品(门票)
if (origin.attribute * 1 === 3) {
dest.tickets = true;
dest.ticketsConfirm = helpers.urlFormat('/cart/index/ticketsConfirm');
// 展览票
if (origin.erpProductId * 1 === SINGLE_TICKETS_SKN) {
// 展览票
if (origin.productSkn * 1 === SINGLE_TICKETS_SKN) {
dest.single = true;
} else {
// 套票
... ... @@ -619,12 +975,12 @@ const _getCommonConsult = () => {
let getProductData = (data) => {
let finalResult;
let params = {
method: 'h5.product.data'
method: 'app.product.data'
};
if (data.id) { // 通过 productId 获取商品详情
Object.assign(params, {
productId: _.toString(data.id)
product_id: _.toString(data.id)
});
} else if (data.productSkn) { // 通过 productSkn 获取商品详情
Object.assign(params, {
... ... @@ -643,22 +999,22 @@ let getProductData = (data) => {
params.current_vip_level = data.vipLevel;
return api.get('', params, {
cache: false
}).then(result => {
if (result.code === 500) {
code: 200
}).then(global.yoho.camelCase).then(result => {
if (result.code === 500 || !result.data) {
return {};
}
result = result.data;
return Promise.all([
_getShopsInfo(result.brandId),
_getPromotionInfo(result.erpProductId),
_getShopsInfo(result.brandInfo.brandId),
_getPromotionInfo(result.productSkn),
comment.getCommentInfo({
productId: result.id
productId: result.productId
}),
_getCommonConsult(),
comment.getConsults(result.id, 1, 2)
comment.getConsults(result.productId, 1, 2)
]).then((info) => {
result.promotionBoList = info[1];
finalResult = _detailDataPkg(result, data.uid, data.vipLevel, data.ua);
finalResult.enterStore = info[0];
finalResult.isStudent = data.isStudent;
... ... @@ -681,7 +1037,6 @@ let getProductData = (data) => {
consults: _.take(info[3], 2)
});
}
return finalResult;
});
... ...
... ... @@ -6,7 +6,7 @@
*/
'use strict';
const $ = require('cheerio');
const _ = require('lodash');
// const log = require(`${library}/logger`);
... ... @@ -339,6 +339,25 @@ let getintroData = (data, req) => {
});
};
let getintroIntro = (data) => {
return api.get('', {
method: 'app.product.intro',
product_skn: data.productskn
}).then(result => {
result = $.load(result);
result = result('#productDesc');
return (result.html() || '').replace(/<img src=/g, '<img class="lazy" src="data:image/gif;' +
'base64,R0lGODlhAQABAJEAAAAAAP///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="' +
' data-original=').replace(/<img border="0" src=/g, '<img border="0" class="lazy" ' +
'src="data:image/gif;base64,' +
'R0lGODlhAQABAJEAAAAAAP///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="' +
' data-original=');
});
};
module.exports = {
getintroData
getintroData,
getintroIntro
};
... ...
... ... @@ -73,7 +73,7 @@
</div>
{{/if}}
{{#if studentCoinNum}}
{{!-- {{#if studentCoinNum}}
<div class="price-date">
<div class="student-price">
<i class="student-name">返</i> 学生返币{{ studentCoinNum}}个
... ... @@ -87,7 +87,7 @@
</div>
</div>
{{/commodityReturn}}
{{/if}}
{{/if}} --}}
{{#unless studentPrice}}
{{# vipLevel}}
... ...
... ... @@ -16,9 +16,9 @@ module.exports = {
siteUrl: '//m.yohobuy.com',
assetUrl: '//localhost:5001',
domains: {
api: 'http://api.yoho.yohoops.org/',
service: 'http://service.yoho.yohoops.org/',
liveApi: 'http://api.live.yoho.cn/'
api: 'http://api.yoho.cn/',
service: 'http://service.yoho.cn/',
liveApi: 'http://testapi.live.yohops.com:9999/'
},
subDomains: {
host: '.m.yohobuy.com',
... ...
/**
* Created by PhpStorm.
* User: Targaryen
* Date: 2016/8/29
* Time: 15:27
*/
'use strict';
module.exports = () => {
// 页面seo相关属性的配置文件
const seoMap = require('./seoConfig');
/**
* 设置seo相关的数据,包括title, keywords, description
* @param {undefined}
* @return {Function} 中间件函数,用于给res.locals对象添加属性
*/
module.exports = () => {
return (req, res, next) => {
if (req.subdomains.length) {
req.url = '/product/index/brand?domain=' + req.subdomains[0];
if (!req.xhr) {
Object.assign(res.locals, seoMap[req.path] || seoMap['/']);
}
next();
};
};
... ...
const seoMap = {
/* eslint-disable */
'/': {
title: 'YOHO!BUY 有货 | 年轻人潮流购物中心,中国潮流购物风向标,官方授权正品保证',
keywords: 'Yoho! 有货官网,潮流志,潮流男装,潮牌,美国潮牌,日本潮牌,香港潮牌,潮牌店,新品首发,欧美潮流,全球购,代购,时尚,流行,特卖,B2C,正品,购物网站,网上购物,货到付款',
description: 'YOHO!BUY有货,年轻人潮流购物中心,中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。作为YOHO!旗下的购物平台,汇集了全球潮流时尚商品和中国最流行的商品,也是国内最大的原创文化商品平台,也是香港,台湾地区流行商品的集中地。同时包含日本、韩国等众多国外潮流品牌,带给您全新潮流购物体验。'
},
'/woman': {
title: '女生|时尚潮流女装,日韩女装,潮牌女装全球购|YOHO!BUY有货 100%正品保证',
keywords: '女生服饰,时尚潮流女装,日韩女装,女装正品购物网站,女装全球购',
description: 'YOHO!BUY有货官网女生频道汇集了全球女装潮流时尚,提供时尚潮流女装,日版女装,韩版女装,潮牌女装正品全球购。YOHO!BUY有货购物100%正品保证,支持货到付款。'
},
'/kids': {
title: '潮童|男童装,女童装,韩版童装,儿童服装服饰|YOHO!BUY有货 100%正品保证',
keywords: '潮童,男童装,女童装,韩版童装,儿童服装服饰',
description: 'YOHO!BUY有货官网潮童频道汇集了全球潮童潮流时尚,提供新款男童装,女童装,韩版童装,儿童服装服饰正品全球购。YOHO!BUY有货购物100%正品保证,支持货到付款。'
},
'/lifestyle': {
title: '创意生活|创意生活馆,潮流创意家居,家居生活用品|YOHO!BUY 有货 100%正品保证',
keywords: '创意生活,创意生活馆,潮流家居,潮流创意家居,家居生活用品,YOHO!BUY有货',
description: 'YOHO!BUY有货官网创意生活频道汇集了创意生活馆,潮流创意家居,家居生活用品等正品网购,给您的生活带来更多创意。YOHO!BUY有货购物100%正品保证,支持货到付款。'
}
/* eslint-enable */
};
module.exports = seoMap;
... ...
/**
* 匹配subdomain
* @author: 杨延青<yanqing.yang@yoho.cn>
* @date: 2016/6/16
*/
'use strict';
const querystring = require('querystring');
const helpers = global.yoho.helpers;
module.exports = () => {
return (req, res, next) => {
console.log();
if (req.subdomains.length) {
switch (req.subdomains[0]) {
case 'guang': // 逛
req.url = req.url.replace('/guang', '');
req.url = `/guang${req.url}`;
break;
case 'list': // list
req.url = `/product/index/index?${querystring.stringify(req.query)}`;
break;
case 'search': // search
req.url = `/product/search/list?${querystring.stringify(req.query)}`;
break;
case 'sale': // sale
res.redirect(301, helpers.urlFormat('/product/sale', req.query, 'default'));
return;
case 'new': // new.m.yohobuy.com 全部跳转到 m.yohobuy.com
res.redirect(301, helpers.urlFormat(req.path, req.query, 'default'));
return;
case 'item': // item.m.yohobuy.com 全部跳转到 m.yohobuy.com
res.redirect(301, helpers.urlFormat(req.path, req.query, 'default'));
return;
default: // 其它(识别为品牌)
req.url = `/product/index/brand?domain=${req.subdomains[0]}`;
break;
}
}
next();
};
};
\ No newline at end of file
... ...
{
"name": "m-yohobuy-node",
"version": "4.9.24",
"version": "4.9.25",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ... @@ -31,13 +31,14 @@
},
"license": "MIT",
"dependencies": {
"bluebird": "^3.4.1",
"bluebird": "^3.4.3",
"body-parser": "^1.15.2",
"cheerio": "^0.22.0",
"connect-memcached": "^0.2.0",
"cookie-parser": "^1.4.3",
"express": "^4.14.0",
"express-handlebars": "^3.0.0",
"express-session": "^1.14.0",
"express-session": "^1.14.1",
"influxdb-winston": "^1.0.1",
"lodash": "^4.15.0",
"md5": "^2.1.0",
... ... @@ -54,14 +55,14 @@
"serve-favicon": "^2.3.0",
"uuid": "^2.0.2",
"winston": "^2.2.0",
"winston-daily-rotate-file": "^1.2.0",
"winston-daily-rotate-file": "^1.3.0",
"yoho-node-lib": "0.0.49"
},
"devDependencies": {
"autoprefixer": "^6.3.7",
"ava": "^0.16.0",
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.9.0",
"babel-preset-es2015": "^6.14.0",
"babel-register": "^6.14.0",
"eslint": "^3.0.1",
"eslint-config-yoho": "^1.0.1",
"gulp": "^3.9.1",
... ... @@ -71,11 +72,11 @@
"gulp-sourcemaps": "^2.0.0-alpha",
"gulp-util": "^3.0.7",
"husky": "^0.11.4",
"nodemon": "^1.10.0",
"nodemon": "^1.10.2",
"nyc": "^8.1.0",
"postcss-assets": "^4.0.1",
"postcss-cachebuster": "^0.1.3",
"postcss-calc": "^5.2.1",
"postcss-calc": "^5.3.1",
"postcss-center": "^1.0.0",
"postcss-clearfix": "^1.0.0",
"postcss-crip": "^2.0.0",
... ... @@ -89,8 +90,8 @@
"shelljs": "^0.7.0",
"stylelint": "^7.1.0",
"stylelint-config-yoho": "^1.2.7",
"webpack": "^1.13.1",
"webpack-dev-server": "^1.14.1",
"webpack": "^1.13.2",
"webpack-dev-server": "^1.15.0",
"webpack-stream": "^3.1.0",
"yoho-fastclick": "^1.0.6",
"yoho-hammer": "^2.0.7",
... ...
/**
* 分类
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2015/10/14
*/
var $ = require('yoho-jquery');
var $nav = $('.category-nav'),
$categoryContainer = $('.category-container'),
$contents = $categoryContainer.children('.content'),
$subLevelItem = $categoryContainer.find('.sub-level li'),
$primaryItem = $categoryContainer.find('.primary-level li');
require('../common');
(function() {
var $header = $('.yoho-header'),
$search = $('#search-input');
var h = $(window).height() - $header.outerHeight() - $search.outerHeight() - $nav.outerHeight();
$categoryContainer.css('min-height', h);
$contents.height(h);
}());
$('#search-input').focus(function() {
$(this).blur();
});
$nav.on('contextmenu', function() {
return false;
});
$('.category-container').on('contextmenu', function() {
return false;
});
$nav.on('touchend touchcancel', function(e) {
var $this = $(e.target).closest('li'),
selector = '.' + $this.data('channel');
if ($this.hasClass('focus')) {
return;
}
$nav.find('li.focus').removeClass('focus');
$this.addClass('focus');
$contents.addClass('hide');
$contents.filter(selector).removeClass('hide');
});
$categoryContainer.on('touchend', function(e) {
var $this = $(e.target),
$subLevel,
$cur, index;
$cur = $this.closest('.p-level-item');
if ($cur.length > 0) {
index = $cur.index();
$subLevel = $this.closest('.content').find('.sub-level');
if ($this.hasClass('focus')) {
return;
}
$this.closest('.primary-level').children('.focus').removeClass('focus');
$this.addClass('focus');
$subLevel.not('.hide').addClass('hide');
$subLevel.eq(index).removeClass('hide');
}
});
$categoryContainer.find('.primary-level').on('touchstart touchend touchcancel', 'li', function() {
$primaryItem.removeClass('highlight');
$(this).addClass('highlight');
}).on('touchend touchcancel', 'li', function() {
$(this).removeClass('highlight');
});
$categoryContainer.find('.sub-level').on('touchstart', 'li', function() {
$subLevelItem.removeClass('highlight');
$(this).addClass('highlight');
}).on('touchend touchcancel', 'li', function() {
$(this).removeClass('highlight');
});
$nav.on('touchstart', 'li', function() {
$nav.find('li').removeClass('bytouch');
$(this).addClass('bytouch');
}).on('touchend touchcancel', 'li', function() {
$nav.find('li').removeClass('bytouch');
});
... ...
.category-page {
font-size: 30px;
.search-input {
position: relative;
background-color: #f8f8f8;
padding: 13px 20px;
p {
box-sizing: border-box;
width: 100%;
height: 60px;
line-height: 60px;
border: none;
padding-left: 66px;
border-radius: 60px;
font-size: 26px;
background: #fff;
color: #999;
}
}
.search-icon {
position: absolute;
top: 0;
bottom: 0;
left: 43px;
line-height: 86px;
color: #999;
}
.category-nav {
height: 70px;
border-bottom: 1px solid #e6e6e6;
li {
display: block;
box-sizing: border-box;
float: left;
height: 100%;
padding: 20px 0;
width: 25%;
text-align: center;
color: #999;
&:last-child {
border-right: none;
}
&.focus {
color: #000;
}
&.bytouch{
background:#eee;
}
}
span {
display: block;
width: 100%;
height: 30px;
line-height: 30px;
font-size: 30px;
border-right: 1px solid #e6e6e6;
}
li:last-child span {
border-right: 0;
}
}
.content {
background: #f8f8f8;
&.hide {
display: none;
}
}
.primary-level {
float: left;
box-sizing: border-box;
width: 45%;
> li {
height: 89px;
line-height: 89px;
padding: 0 32px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&.focus {
background-color: #fff;
}
&.highlight {
background-color: #eee;
}
}
}
.sub-level-container {
float: left;
box-sizing: border-box;
background: #fff;
width: 55%;
height: 100%;
}
.sub-level {
width: 100%;
&.hide {
display: none;
}
> li {
box-sizing: border-box;
height: 89px;
line-height: 89px;
border-bottom: 1px solid #e6e6e6;
padding-left: 20px;
&.highlight {
background: #eee;
}
&:last-child {
border-bottom: none;
}
}
a {
display: block;
height: 100%;
width: 100%;
color: #000;
}
}
}
... ...
... ... @@ -23,6 +23,7 @@
@import "coupon";
@import "discount-list";
@import "left-right";
@import "cate";
@import "three-picture";
.mobile-container {
... ...
... ... @@ -6,7 +6,12 @@
.service {
height: 28px;
background: resolve("product/service.png") no-repeat;
background-size: cover;
background-size: 100%;
&.not-support-exchange {
background: resolve("product/not-support-exchange.png") no-repeat;
background-size: 100%;
}
}
.detail {
... ...