Authored by ccbikai

Merge branch 'feature/product-detail' into release/4.9

@@ -7,6 +7,6 @@ @@ -7,6 +7,6 @@
7 <input id="phone-num" class="input phone-num" type="text" placeholder="手机号"> 7 <input id="phone-num" class="input phone-num" type="text" placeholder="手机号">
8 </div> 8 </div>
9 <span id="btn-next" class="btn btn-next disable row">下一步</span> 9 <span id="btn-next" class="btn btn-next disable row">下一步</span>
10 - <p class="register-tip">YOHO!Family账号可登录Yoho!Buy有货、YOHO!Boys、YOHO!Girls及SHOW</p> 10 + <p class="register-tip">Yoho!Family账号可登录Yoho!Buy有货、Yoho!Now、mars及SHOW</p>
11 </div> 11 </div>
12 </div> 12 </div>
@@ -51,6 +51,39 @@ exports.index = (req, res, next) => { @@ -51,6 +51,39 @@ exports.index = (req, res, next) => {
51 }; 51 };
52 52
53 /** 53 /**
  54 + * 商品基本信息 SKN 进入
  55 + * @param {[type]} req [description]
  56 + * @param {[type]} res [description]
  57 + * @return {[type]} [description]
  58 + */
  59 +exports.indexSkn = (req, res, next) => {
  60 + if (!req.params[0]) {
  61 + return next();
  62 + }
  63 + let uid = req.user.uid || 0;
  64 + let headerData = headerModel.setNav({
  65 + navTitle: '商品详情'
  66 + });
  67 +
  68 + detailModel.getProductData({
  69 + productSkn: req.params[0],
  70 + uid: uid,
  71 + ua: req.get('user-agent') || ''
  72 + }).then((result) => {
  73 + if (_.isEmpty(result)) {
  74 + return next();
  75 + }
  76 + res.render('detail/detail', {
  77 + pageHeader: headerData,
  78 + result: result,
  79 + page: 'detail',
  80 + title: result.goodsName,
  81 + pageFooter: true
  82 + });
  83 + }).catch(next);
  84 +};
  85 +
  86 +/**
54 * 商品尺码信息详情 87 * 商品尺码信息详情
55 * @param {[type]} req [description] 88 * @param {[type]} req [description]
56 * @param {[type]} res [description] 89 * @param {[type]} res [description]
@@ -571,10 +571,19 @@ const _getCommonConsult = () => { @@ -571,10 +571,19 @@ const _getCommonConsult = () => {
571 let getProductData = (data) => { 571 let getProductData = (data) => {
572 let finalResult; 572 let finalResult;
573 let params = { 573 let params = {
574 - productId: _.toString(data.id),  
575 method: 'h5.product.data' 574 method: 'h5.product.data'
576 }; 575 };
577 576
  577 + if (data.id) { // 通过 productId 获取商品详情
  578 + Object.assign(params, {
  579 + productId: _.toString(data.id)
  580 + });
  581 + } else if (data.productSkn) { // 通过 productSkn 获取商品详情
  582 + Object.assign(params, {
  583 + product_skn: _.toString(data.productSkn)
  584 + });
  585 + }
  586 +
578 if (!_.isEmpty(data.uid)) { 587 if (!_.isEmpty(data.uid)) {
579 params.uid = data.uid; 588 params.uid = data.uid;
580 } 589 }
@@ -124,6 +124,7 @@ const _convertActicityData = (data) => { @@ -124,6 +124,7 @@ const _convertActicityData = (data) => {
124 124
125 data = data || []; 125 data = data || [];
126 _.forEach(data, (item) => { 126 _.forEach(data, (item) => {
  127 + if (item.promotionName) {
127 discountArr = item.promotionName.split('~'); 128 discountArr = item.promotionName.split('~');
128 if (discountArr.length === 1) { 129 if (discountArr.length === 1) {
129 discountNum = _transDiscountToArr(discountArr[0])[0]; 130 discountNum = _transDiscountToArr(discountArr[0])[0];
@@ -145,6 +146,7 @@ const _convertActicityData = (data) => { @@ -145,6 +146,7 @@ const _convertActicityData = (data) => {
145 leftTime: item.startLeftTime > 0 ? dateFormate(item.startTime) : item.endLeftTime, 146 leftTime: item.startLeftTime > 0 ? dateFormate(item.startTime) : item.endLeftTime,
146 hide: false 147 hide: false
147 }); 148 });
  149 + }
148 }); 150 });
149 151
150 return formatData; 152 return formatData;
@@ -28,6 +28,8 @@ const recommendForYou = require(`${cRoot}/recommend-for-you`); @@ -28,6 +28,8 @@ const recommendForYou = require(`${cRoot}/recommend-for-you`);
28 28
29 // /pro_136349_455445/HEARTSOFARMianMaShuJiaoXiuXianKuPS1684.html 29 // /pro_136349_455445/HEARTSOFARMianMaShuJiaoXiuXianKuPS1684.html
30 router.get(/\/pro_([\d]+)_([\d]+)\/(.*)/, detail.index); // 商品详情页 30 router.get(/\/pro_([\d]+)_([\d]+)\/(.*)/, detail.index); // 商品详情页
  31 +// /show_51047967.html
  32 +router.get(/\/show_([\d]+)/, detail.indexSkn); // 商品详情页 SKN 进入
31 router.get('/detail/intro/:productskn', detail.intro); // 商品内嵌页 33 router.get('/detail/intro/:productskn', detail.intro); // 商品内嵌页
32 router.get('/detail/preference', detail.preference); // 为你优选 34 router.get('/detail/preference', detail.preference); // 为你优选
33 router.get('/detail/consults', detail.consults); // 商品咨询页 35 router.get('/detail/consults', detail.consults); // 商品咨询页
@@ -34,9 +34,9 @@ module.exports = { @@ -34,9 +34,9 @@ module.exports = {
34 useOneapm: false, 34 useOneapm: false,
35 useCache: false, 35 useCache: false,
36 memcache: { 36 memcache: {
37 - master: ['192.168.102.222:12111'],  
38 - slave: ['192.168.102.222:12111'],  
39 - session: ['192.168.102.222:12111'], 37 + master: ['192.168.102.205:12111'],
  38 + slave: ['192.168.102.205:12111'],
  39 + session: ['192.168.102.205:12111'],
40 timeout: 1000, 40 timeout: 1000,
41 retries: 0 41 retries: 0
42 }, 42 },
@@ -4,8 +4,14 @@ @@ -4,8 +4,14 @@
4 <div class="center-square"> 4 <div class="center-square">
5 <div class="title">{{title}}</div> 5 <div class="title">{{title}}</div>
6 <div class="num"><span class="discount-num">{{discountNum}}</span> {{discountText}}</div> 6 <div class="num"><span class="discount-num">{{discountNum}}</span> {{discountText}}</div>
7 - {{>product/outlet/countdown}} 7 + <div id="demo1">
  8 + <!--默认,服务端输出leftTime,把客户端时间干扰降到最低。单位秒-->
  9 + <div class="cd cd-lite time hide" data-config="{'leftTime': {{leftTime}} }">— 仅剩<span class="left-day">${d}天</span>${h}时${m}分${s}秒 —</div>
  10 + <div class="cd cd-medium time hide">{{leftTime}}</div>
  11 + </div>
8 </div> 12 </div>
9 </a> 13 </a>
10 </div> 14 </div>
  15 +{{#if @last}}
11 <div class="more-activity hide"><p>更多精彩活动</p><span class="iconfont count-down-icon">&#xe609;</span></div> 16 <div class="more-activity hide"><p>更多精彩活动</p><span class="iconfont count-down-icon">&#xe609;</span></div>
  17 +{{/if}}
@@ -19,7 +19,8 @@ module.exports = function(specificGender) { @@ -19,7 +19,8 @@ module.exports = function(specificGender) {
19 page = 0, 19 page = 0,
20 gender = null, 20 gender = null,
21 num, 21 num,
22 - url; 22 + url,
  23 + RECPOSE = '';
23 24
24 // The kidsType can be specified by the parameter. Add by @ZhaoBiao 25 // The kidsType can be specified by the parameter. Add by @ZhaoBiao
25 var kidsType = specificGender === 'kids' || $('.mobile-wrap').hasClass('kids-wrap') ? true : false, 26 var kidsType = specificGender === 'kids' || $('.mobile-wrap').hasClass('kids-wrap') ? true : false,
@@ -49,6 +50,13 @@ module.exports = function(specificGender) { @@ -49,6 +50,13 @@ module.exports = function(specificGender) {
49 url = '/product/recom/maylike?gender=' + gender; 50 url = '/product/recom/maylike?gender=' + gender;
50 } 51 }
51 52
  53 + // 首页男生和女生,推荐位ID,埋点
  54 + if (window.location.pathname === '/boys') {
  55 + RECPOSE = 110001;
  56 + } else if (window.location.pathname === '/girls') {
  57 + RECPOSE = 110002;
  58 + }
  59 +
52 $curNav = $navList.children('.focus'); 60 $curNav = $navList.children('.focus');
53 61
54 if (lifestyleType) { 62 if (lifestyleType) {
@@ -99,6 +107,7 @@ module.exports = function(specificGender) { @@ -99,6 +107,7 @@ module.exports = function(specificGender) {
99 page: page + 1 107 page: page + 1
100 }, 108 },
101 success: function(data) { 109 success: function(data) {
  110 + var PRDID = [];
102 111
103 if (data === ' ') { 112 if (data === ' ') {
104 loading.hideLoadingMask(); 113 loading.hideLoadingMask();
@@ -158,6 +167,21 @@ module.exports = function(specificGender) { @@ -158,6 +167,21 @@ module.exports = function(specificGender) {
158 167
159 $title[0].mlellipsis(2); 168 $title[0].mlellipsis(2);
160 }); 169 });
  170 +
  171 + // 为您优选埋点 start
  172 + PRDID = [];
  173 + $(data).closest('.good-info').each(function() {
  174 + PRDID.push($(this).data('id'));
  175 + });
  176 + window.givePoint({
  177 + REC_POSE: RECPOSE,
  178 + PRD_ID: PRDID.join(','),
  179 + PRD_NUM: $(data).closest('.good-info').length,
  180 + ACTION_ID: 0,
  181 + page_num: page
  182 + });
  183 +
  184 + // 为您优选埋点 end
161 }, 185 },
162 error: function() { 186 error: function() {
163 tip.show('网络断开连接了~'); 187 tip.show('网络断开连接了~');
@@ -186,4 +210,21 @@ module.exports = function(specificGender) { @@ -186,4 +210,21 @@ module.exports = function(specificGender) {
186 $(window).scroll(function() { 210 $(window).scroll(function() {
187 window.requestAnimationFrame(scrollHandler); 211 window.requestAnimationFrame(scrollHandler);
188 }); 212 });
  213 +
  214 + // 为您优选埋点
  215 + $('.maybe-like .goods-list').on('click', 'a', function() {
  216 +
  217 + var pageNum = 50;
  218 +
  219 + index = $(this).closest('.good-info').index() + 1;
  220 +
  221 + window.givePoint({
  222 + REC_POSE: RECPOSE,
  223 + PRD_ID: $(this).closest('.good-info').data('id'),
  224 + PRD_NUM: index % pageNum === 0 ? pageNum : index % pageNum,
  225 + ACTION_ID: 1,
  226 + page_num: Math.ceil(index / pageNum)
  227 + });
  228 + return true;
  229 + });
189 }; 230 };
@@ -10,6 +10,10 @@ var $footer = $('#yoho-footer'), @@ -10,6 +10,10 @@ var $footer = $('#yoho-footer'),
10 $yohoPage = $('.yoho-page'), 10 $yohoPage = $('.yoho-page'),
11 $header = $('.yoho-header'); 11 $header = $('.yoho-header');
12 12
  13 +// 为您优选-40位随机数指纹请求id
  14 +var RECID = (new Date().getTime() + '_H5_YOHOBUY_' + Math.floor(Math.random() * 1000000 + 1000000) +
  15 + '_' + Math.floor(Math.random() * 1000000 + 1000000));
  16 +
13 function cookie(name) { 17 function cookie(name) {
14 var cookies = document.cookie, 18 var cookies = document.cookie,
15 cookieVal, 19 cookieVal,
@@ -294,6 +298,51 @@ if ($footer.find('.user-name').text().length === 11) { @@ -294,6 +298,51 @@ if ($footer.find('.user-name').text().length === 11) {
294 $footer.find('.user-name').html(phoneHidden($footer.find('.user-name').text())); 298 $footer.find('.user-name').html(phoneHidden($footer.find('.user-name').text()));
295 } 299 }
296 300
  301 +// 为您优选埋点 http://redmine.yoho.cn/issues/10117
  302 +function givePoint(parameter) {
  303 + var CID = 1;
  304 +
  305 + if (!window._yas || !window._yas.sendCustomInfo) {
  306 + return false;
  307 + }
  308 +
  309 + // 男:1,女:2,潮童:3,创意生活:4
  310 + switch (cookie('_Channel')) {
  311 + case 'boys':
  312 + CID = 1;
  313 + break;
  314 + case 'girls':
  315 + CID = 2;
  316 + break;
  317 + case 'kids':
  318 + CID = 3;
  319 + break;
  320 + case 'lifestyle':
  321 + CID = 4;
  322 + break;
  323 + }
  324 +
  325 + parameter = $.extend({
  326 + REC_POSE: '',
  327 + REC_ID: RECID,
  328 + PRD_ID: '',
  329 + PRD_NUM: 0,
  330 + C_ID: CID,
  331 + ACTION_ID: 0,
  332 + page_num: 1
  333 + }, parameter);
  334 +
  335 + if (parameter.REC_POSE === '' || parameter.PRD_ID === '') {
  336 + return true;
  337 + }
  338 +
  339 + window._yas.sendCustomInfo({
  340 + op: 'YB_CHOOSE_FOR_YOU_Y',
  341 + uid: getUid(),
  342 + param: JSON.stringify(parameter)
  343 + }, true);
  344 +}
  345 +
297 // 暴露公共接口 346 // 暴露公共接口
298 window.cookie = cookie; 347 window.cookie = cookie;
299 348
@@ -312,3 +361,5 @@ window.rePosFooter = rePosFooter; @@ -312,3 +361,5 @@ window.rePosFooter = rePosFooter;
312 window.reMarginFooter = reMarginFooter; 361 window.reMarginFooter = reMarginFooter;
313 362
314 window.queryString = queryString(); 363 window.queryString = queryString();
  364 +
  365 +window.givePoint = givePoint;
@@ -42,9 +42,10 @@ function initNavScroll(opt) { @@ -42,9 +42,10 @@ function initNavScroll(opt) {
42 } 42 }
43 43
44 // 获取url中的参数 44 // 获取url中的参数
45 -function getUrlParam(name) { 45 +function getUrlParam(name, url) {
46 var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); // 构造一个含有目标参数的正则表达式对象 46 var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); // 构造一个含有目标参数的正则表达式对象
47 - var r = window.location.search.substr(1).match(reg); // 匹配目标参数 47 + var urlTest = url || window.location.href;
  48 + var r = urlTest.slice(urlTest.indexOf('?') + 1).match(reg); // 匹配目标参数
48 49
49 // 返回参数值 50 // 返回参数值
50 if (r !== null) { 51 if (r !== null) {
@@ -76,6 +77,20 @@ function activeNav() { @@ -76,6 +77,20 @@ function activeNav() {
76 var $nav = $('#index_nav'); 77 var $nav = $('#index_nav');
77 var index = getUrlParam('yh_channel'); 78 var index = getUrlParam('yh_channel');
78 79
  80 + // 判断是否有首页选项
  81 + var flag = false;
  82 +
  83 + $nav.find('li').each(function() {
  84 + var $this = $(this);
  85 + var url = $this.find('a').attr('href');
  86 + var code = getUrlParam('content_code', url);
  87 +
  88 + if (code === 'c19ffa03f053f4cac3690b22c8da26b7') {
  89 + flag = true;
  90 + return false;
  91 + }
  92 +
  93 + });
79 getOtherIndex(); 94 getOtherIndex();
80 if (index === null) { 95 if (index === null) {
81 index = getUrlParam('type'); 96 index = getUrlParam('type');
@@ -85,6 +100,9 @@ function activeNav() { @@ -85,6 +100,9 @@ function activeNav() {
85 $nav.find('li[data-type=' + index + ']').addClass('active').siblings().removeClass('active'); 100 $nav.find('li[data-type=' + index + ']').addClass('active').siblings().removeClass('active');
86 } 101 }
87 } else { 102 } else {
  103 + if (!flag) {
  104 + index -= 1;
  105 + }
88 $nav.find('li:not([data-nav="other"])').eq(index).addClass('active').siblings().removeClass('active'); 106 $nav.find('li:not([data-nav="other"])').eq(index).addClass('active').siblings().removeClass('active');
89 } 107 }
90 108
@@ -253,7 +253,7 @@ @@ -253,7 +253,7 @@
253 .messages { 253 .messages {
254 width: 84%; 254 width: 84%;
255 height: 4rem; 255 height: 4rem;
256 - position: absolute; 256 + position: fixed;
257 background: rgba(0, 0, 0, 0.9); 257 background: rgba(0, 0, 0, 0.9);
258 border-radius: 0.6rem; 258 border-radius: 0.6rem;
259 left: 8%; 259 left: 8%;
@@ -14,8 +14,9 @@ @@ -14,8 +14,9 @@
14 display:inline-block; 14 display:inline-block;
15 width: 20px; 15 width: 20px;
16 height: 20px; 16 height: 20px;
17 - background: url('/channel/tip.png') no-repeat;  
18 - background-size: 20px 20px; 17 + background: url('/channel/tip.png');
  18 + background-repeat: no-repeat;
  19 + background-size: contain;
19 vertical-align: -2px; 20 vertical-align: -2px;
20 margin-right: 8px; 21 margin-right: 8px;
21 } 22 }
@@ -32,7 +33,8 @@ @@ -32,7 +33,8 @@
32 33
33 .chan { 34 .chan {
34 background-image: url('/channel/up-icon.png'); 35 background-image: url('/channel/up-icon.png');
35 - background-size: 31px 31px; 36 + background-repeat: no-repeat;
  37 + background-size: contain;
36 position: absolute; 38 position: absolute;
37 top: 50%; 39 top: 50%;
38 margin-top: -15px; 40 margin-top: -15px;