Authored by 郭成尧

merge-master

@@ -23,7 +23,9 @@ exports.index = (req, res, next) => { @@ -23,7 +23,9 @@ exports.index = (req, res, next) => {
23 module: 'activity', 23 module: 'activity',
24 page: 'coupon-floor', 24 page: 'coupon-floor',
25 wechatShare: true, 25 wechatShare: true,
26 - title: '领券中心', 26 + title: '有货优惠券_有货现金优惠券,全场券,品类券,品牌券_领券频道-Yoho!Buy有货',
  27 + keywords: '有货优惠券,有货现金优惠券,全场券,品类券,品牌券',
  28 + description: 'Yoho!Buy有货官网领券频道,提供有货优惠券,有货现金优惠券,有货全场券,有货品类券,品牌券免费领取,让你做网购达人,省钱又省心!',
27 pageHeader: headerModel.setNav({ 29 pageHeader: headerModel.setNav({
28 navTitle: '领券中心' 30 navTitle: '领券中心'
29 }), 31 }),
@@ -22,9 +22,9 @@ @@ -22,9 +22,9 @@
22 style="{{#if param.bgcolor}}background-color:{{param.bgcolor}}{{/if}}"> 22 style="{{#if param.bgcolor}}background-color:{{param.bgcolor}}{{/if}}">
23 {{#if param.bgimg}} 23 {{#if param.bgimg}}
24 {{#isLazyLoad type @index}} 24 {{#isLazyLoad type @index}}
25 - <img class="lazy" data-original="{{image2 param.bgimg q=75}}"> 25 + <img class="lazy" data-original="{{imageslim param.bgimg}}">
26 {{else}} 26 {{else}}
27 - <img src="{{image2 param.bgimg q=75}}"> 27 + <img src="{{imageslim param.bgimg}}">
28 {{/isLazyLoad}} 28 {{/isLazyLoad}}
29 {{/if}} 29 {{/if}}
30 {{#component}} 30 {{#component}}
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 {{#if modalImg}} 34 {{#if modalImg}}
35 <div class="modal"> 35 <div class="modal">
36 <span class="modal-close"></span> 36 <span class="modal-close"></span>
37 - <img class="modal-img lazy" data-original="{{image2 modalImg q=75}}"> 37 + <img class="modal-img lazy" data-original="{{imageslim modalImg}}">
38 </div> 38 </div>
39 {{/if}} 39 {{/if}}
40 {{/isEqualOr}} 40 {{/isEqualOr}}
@@ -66,7 +66,7 @@ @@ -66,7 +66,7 @@
66 <div class="swiper-wrapper"> 66 <div class="swiper-wrapper">
67 {{#list}} 67 {{#list}}
68 <div class="swiper-slide" style="{{styleFormat this percent=1}}"> 68 <div class="swiper-slide" style="{{styleFormat this percent=1}}">
69 - <img src="{{image2 src q=75}}"> 69 + <img src="{{imageslim src}}">
70 <a class="anchor" href="{{#if link}}{{link}}{{else}}javascript:void(0);{{/if}}" fp="{{getAnalysis ../../this @index}}"></a> 70 <a class="anchor" href="{{#if link}}{{link}}{{else}}javascript:void(0);{{/if}}" fp="{{getAnalysis ../../this @index}}"></a>
71 </div> 71 </div>
72 {{/list}} 72 {{/list}}
@@ -88,7 +88,7 @@ @@ -88,7 +88,7 @@
88 88
89 {{#isEqualOr type 'productGroup'}} 89 {{#isEqualOr type 'productGroup'}}
90 {{! 商品池}} 90 {{! 商品池}}
91 - <div class="product-container item{{numOfOneRow}}" {{#if proBgImg}}style="background:url({{image2 proBgImg q=75}}) repeat;background-size:100%;"{{/if}}> 91 + <div class="product-container item{{numOfOneRow}}" {{#if proBgImg}}style="background:url({{imageslim proBgImg}}) repeat;background-size:100%;"{{/if}}>
92 <div class="product-source" condition='{{stringify searchCondition}}' fp="{{getAnalysis ../this @index}}" 92 <div class="product-source" condition='{{stringify searchCondition}}' fp="{{getAnalysis ../this @index}}"
93 {{#unless defaultPros.length}} 93 {{#unless defaultPros.length}}
94 {{#if searchCondition.item}} 94 {{#if searchCondition.item}}
@@ -103,9 +103,9 @@ @@ -103,9 +103,9 @@
103 <div class="feature-product-info {{#if ../searchCondition}}novisible{{/if}}"> 103 <div class="feature-product-info {{#if ../searchCondition}}novisible{{/if}}">
104 <a class="first-part product-detail" href='{{producturl}}'> 104 <a class="first-part product-detail" href='{{producturl}}'>
105 <div class="product-detail-imgbox"> 105 <div class="product-detail-imgbox">
106 - {{#if ../lefTopImg}}<img class="leftopimg lazy" data-original="{{image2 ../lefTopImg q=75}}">{{/if}}  
107 - {{#if ../rigTopImg}}<img class="rigtopimg lazy" data-original="{{image2 ../rigTopImg q=75}}">{{/if}}  
108 - <img class="product-detail-img lazy" data-original="{{image2 productimg q=75}}"> 106 + {{#if ../lefTopImg}}<img class="leftopimg lazy" data-original="{{imageslim ../lefTopImg}}">{{/if}}
  107 + {{#if ../rigTopImg}}<img class="rigtopimg lazy" data-original="{{imageslim ../rigTopImg}}">{{/if}}
  108 + <img class="product-detail-img lazy" data-original="{{imageslim productimg}}">
109 </div> 109 </div>
110 {{#isEqualOr ../showPrdName '1'}}<p class="product-name">{{productname}}</p>{{/isEqualOr}} 110 {{#isEqualOr ../showPrdName '1'}}<p class="product-name">{{productname}}</p>{{/isEqualOr}}
111 <div class="product-detail-text"> 111 <div class="product-detail-text">
@@ -128,7 +128,7 @@ @@ -128,7 +128,7 @@
128 <div class="brand-div"> 128 <div class="brand-div">
129 <span class="brand-name"{{#if ../fontColor}}style="color:{{../fontColor}};"{{/if}}>{{brandname}}</span> 129 <span class="brand-name"{{#if ../fontColor}}style="color:{{../fontColor}};"{{/if}}>{{brandname}}</span>
130 </div> 130 </div>
131 - <img class="brand-img lazy" data-original="{{image2 ../brandImg q=75}}"> 131 + <img class="brand-img lazy" data-original="{{imageslim ../brandImg}}">
132 </a> 132 </a>
133 {{/if}} 133 {{/if}}
134 </div> 134 </div>
@@ -137,8 +137,8 @@ @@ -137,8 +137,8 @@
137 <div class="feature-product-info novisible"> 137 <div class="feature-product-info novisible">
138 <a class="first-part product-detail" href=''> 138 <a class="first-part product-detail" href=''>
139 <div class="product-detail-imgbox"> 139 <div class="product-detail-imgbox">
140 - {{#if lefTopImg}}<img class="leftopimg" src="{{image2 lefTopImg q=75}}">{{/if}}  
141 - {{#if rigTopImg}}<img class="rigtopimg" src="{{image2 rigTopImg q=75}}">{{/if}} 140 + {{#if lefTopImg}}<img class="leftopimg" src="{{imageslim lefTopImg}}">{{/if}}
  141 + {{#if rigTopImg}}<img class="rigtopimg" src="{{imageslim rigTopImg}}">{{/if}}
142 <img class="product-detail-img" src=""> 142 <img class="product-detail-img" src="">
143 </div> 143 </div>
144 {{#isEqualOr showPrdName '1'}}<p class="product-name"></p>{{/isEqualOr}} 144 {{#isEqualOr showPrdName '1'}}<p class="product-name"></p>{{/isEqualOr}}
@@ -161,7 +161,7 @@ @@ -161,7 +161,7 @@
161 <div class="brand-div"> 161 <div class="brand-div">
162 <span class="brand-name" {{#if fontColor}}style="color:{{fontColor}};"{{/if}}></span> 162 <span class="brand-name" {{#if fontColor}}style="color:{{fontColor}};"{{/if}}></span>
163 </div> 163 </div>
164 - <img class="brand-img" src="{{image2 brandImg q=75}}"> 164 + <img class="brand-img" src="{{imageslim brandImg}}">
165 </a> 165 </a>
166 {{/if}} 166 {{/if}}
167 </div> 167 </div>
@@ -251,7 +251,6 @@ const index = (req, res, next) => { @@ -251,7 +251,6 @@ const index = (req, res, next) => {
251 console.log(data); 251 console.log(data);
252 res.render('info/index', Object.assign({ 252 res.render('info/index', Object.assign({
253 page: 'info-index', 253 page: 'info-index',
254 - title: '逛',  
255 gender: gender, 254 gender: gender,
256 wechatShare: true, 255 wechatShare: true,
257 isWeixin: isWeixin, 256 isWeixin: isWeixin,
@@ -67,7 +67,9 @@ const editor = (req, res, next) => { @@ -67,7 +67,9 @@ const editor = (req, res, next) => {
67 67
68 res.render('index/list', Object.assign({ 68 res.render('index/list', Object.assign({
69 page: 'index-editor', 69 page: 'index-editor',
70 - title: title, 70 + title: `潮流编辑${authorData.data.name}|YOHO!BUY有货`,
  71 + keywords: `潮流编辑${authorData.data.name}`,
  72 + description: `YOHO!BUY有货潮流编辑${authorData.data.name}!`,
71 guangList: true, 73 guangList: true,
72 gender: gender, 74 gender: gender,
73 guang: { 75 guang: {
@@ -193,7 +195,9 @@ const index = (req, res, next) => { @@ -193,7 +195,9 @@ const index = (req, res, next) => {
193 let responseData = { 195 let responseData = {
194 module: 'guang', 196 module: 'guang',
195 page: 'index', 197 page: 'index',
196 - title: '逛 | Yoho!Buy有货 | 潮流购物逛不停', 198 + title: '逛|逛潮流,逛购物,官方授权正品潮流购物中心|YOHO!BUY有货',
  199 + keywords: '逛,逛潮流,逛购物',
  200 + description: 'YOHO!BUY有货逛频道,来YOHO!玩潮流!潮搭大解析!年轻人潮流购物中心,中国潮流购物风向标,吴亦凡重磅代言!YOHO!BUY有货100%正品保证,支持货到付款。',
197 showFooterTab: footerModel.getUrlData('guang') 201 showFooterTab: footerModel.getUrlData('guang')
198 }; 202 };
199 203
@@ -238,7 +242,9 @@ const tag = (req, res, next) => { @@ -238,7 +242,9 @@ const tag = (req, res, next) => {
238 pageHeader: headerData, 242 pageHeader: headerData,
239 module: 'guang', 243 module: 'guang',
240 page: 'index-editor', 244 page: 'index-editor',
241 - title: tagTitle + ' | Yoho!Buy有货 | 潮流购物逛不停' 245 + title: tagTitle + ' | Yoho!Buy有货 | 潮流购物逛不停',
  246 + keywords: tagTitle,
  247 + description: 'YOHO!BUY有货潮流' + tagTitle + '!'
242 }; 248 };
243 249
244 let param = { 250 let param = {
@@ -12,6 +12,7 @@ const star = require(cRoot + '/star'); @@ -12,6 +12,7 @@ const star = require(cRoot + '/star');
12 const homeController = require(`${cRoot}/index`); 12 const homeController = require(`${cRoot}/index`);
13 const plusstar = require(cRoot + '/plusstar'); 13 const plusstar = require(cRoot + '/plusstar');
14 const rewrite = require('../../doraemon/middleware/rewrite'); 14 const rewrite = require('../../doraemon/middleware/rewrite');
  15 +const mip = require('../../doraemon/middleware/mip');
15 16
16 const index = require(cRoot + '/index'); 17 const index = require(cRoot + '/index');
17 const opt = require(cRoot + '/opt'); 18 const opt = require(cRoot + '/opt');
@@ -52,7 +53,7 @@ router.post('/opt/collectArticle', opt.collectArticle); // 资讯文章收藏 (H @@ -52,7 +53,7 @@ router.post('/opt/collectArticle', opt.collectArticle); // 资讯文章收藏 (H
52 router.post('/opt/favoriteBrand', opt.favoriteBrand); // 品牌收藏 53 router.post('/opt/favoriteBrand', opt.favoriteBrand); // 品牌收藏
53 54
54 router.get('/info/index', rewrite.channel, detail.indexRedirect); // 逛详情页 55 router.get('/info/index', rewrite.channel, detail.indexRedirect); // 逛详情页
55 -router.get(/^\/info\/(.*?)\.html/, rewrite.resolve, detail.index); // 逛详情页 SEO优化 56 +router.get(/^\/info\/(.*?)\.html/, rewrite.resolve, mip, detail.index); // 逛详情页 SEO优化
56 57
57 router.get('/:id.html', detail.index); // 逛详情页(兼容 PC 跳转过来的链接) 58 router.get('/:id.html', detail.index); // 逛详情页(兼容 PC 跳转过来的链接)
58 router.get('/info/mini', detail.mini); // 逛mini内容页 59 router.get('/info/mini', detail.mini); // 逛mini内容页
  1 +'use strict';
  2 +const css = require('../css');
  3 +
  4 +// const mipUtils = require('../mip-utils');
  5 +const helpers = global.yoho.helpers;
  6 +const _ = require('lodash');
  7 +const co = require('bluebird').coroutine;
  8 +const stringProcess = require(`${global.utils}/string-process`);
  9 +const guangProcess = require(`${global.utils}/guang-process`);
  10 +const mRoot = '../models';
  11 +const DetailModel = require(`${mRoot}/guang`);
  12 +const typeLib = require('../../../config/type-lib');
  13 +const channels = {
  14 + boys: 1,
  15 + girl: 2,
  16 + kids: 3,
  17 + lifestyle: 4
  18 +};
  19 +
  20 +// const testStr = '';
  21 +
  22 +/**
  23 + * [处理品牌数据]
  24 + * @param {[array]} getBrand [品牌原数据]
  25 + */
  26 +const _relatedBrand = (getBrand, isApp) => {
  27 + let relatedBrand = getBrand;
  28 +
  29 + relatedBrand.forEach(brand => {
  30 + brand.thumb = brand.thumb.replace('http://', '//');
  31 +
  32 + if (isApp) {
  33 + brand.url = brand.url + '?openby:yohobuy={"action":"go.brand","params":{"brand_id":"' + brand.id + '"}}';
  34 + }
  35 + });
  36 +
  37 + return relatedBrand;
  38 +};
  39 +
  40 +/**
  41 + * [处理标签数据]
  42 + * @param {[array]} tags [标签原数据]
  43 + * @param {[Boolean]} isApp [是否app]
  44 + */
  45 +const _relatedTag = (tags, isApp) => {
  46 + let relatedTag = [];
  47 + let tagUrl;
  48 +
  49 + tags.forEach(value => {
  50 +
  51 + tagUrl = helpers.urlFormat('/tags/index', {query: value.name}, 'guang');
  52 +
  53 + if (!isApp) {
  54 + value.url = tagUrl;
  55 + } else {
  56 + if (value.url.indexOf('openby') >= 0) {
  57 + value.url = value.url;
  58 + } else {
  59 + value.url = tagUrl + '&openby:yohobuy={"action":"go.h5","params":{"query":"' + value.name + '","type":0,"title":"' + value.name + '","url":"http://guang.m.yohobuy.com/tags/index","islogin":"N"}}';
  60 + }
  61 + }
  62 +
  63 + relatedTag.push(value);
  64 + });
  65 +
  66 + return relatedTag;
  67 +};
  68 +
  69 +/**
  70 + * [处理相关文章数据]
  71 + * @param {[array]} getOtherArticle [相关文章原数据]
  72 + * @param {[Boolean]} isApp [是否app]
  73 + */
  74 +const _relatedInfo = (getOtherArticle, isApp) => {
  75 + let relatedInfo = [];
  76 + let articleUrl;
  77 +
  78 + getOtherArticle.forEach(value => {
  79 + articleUrl = helpers.urlFormat('/info/index', {
  80 + id: value.id
  81 + }, 'guang');
  82 +
  83 + if (isApp) {
  84 + value.url = articleUrl + '&openby:yohobuy={"action":"go.h5","params":{"id":"' + value.id + '","shareparam":{"id":"' + value.id + '"},"islogin":"N","type":1,"url":"http://guang.m.yohobuy.com/info/index","param":{"id":"' + value.id + '"}}}';
  85 + } else {
  86 + value.url = articleUrl;
  87 + }
  88 +
  89 + value.thumb = helpers.image(value.thumb, 279, 175);
  90 + relatedInfo.push(value);
  91 + });
  92 +
  93 + return relatedInfo;
  94 +};
  95 +
  96 +const detailIndex = (req, res, next) => {
  97 + let id = req.query.id || req.params[0] || req.params.id,
  98 + gender = req.query.gender ||
  99 + req.query.channel && typeLib.channels[req.query.channel] ||
  100 + req.cookies._Channel && channels[req.cookies._Channel] ||
  101 + 1,
  102 + isApp = req.query.app_version || req.query.appVersion || false, // 标识是不是APP访问的
  103 + isWeixin = req.yoho.isWechat,
  104 + channel = req.query.channel || req.cookies._Channel,
  105 + isqq = req.yoho.isqq,
  106 + isWeibo = req.yoho.isWeibo,
  107 + isMip = true,
  108 + isShare;
  109 +
  110 + res.locals.appPath = `yohobuy://yohobuy.com/goapp?openby:yohobuy={"action":"go.h5","params":{"id":"${id}","share":"/guang/api/v1/share/guang?id=${id}","shareparam":{"id":"${id}"},"islogin":"N","type":1,"url":"http://guang.m.yohobuy.com/info/index","param":{"id":"${id}"}}}`;
  111 +
  112 + // 判断参数是否有效, 无效会跳转到错误页面
  113 + if (!stringProcess.isNumeric(id)) {
  114 + res.json({
  115 + code: 400,
  116 + message: '非法请求',
  117 + data: ''
  118 + });
  119 + return;
  120 + }
  121 +
  122 + isShare = isWeixin || isqq || isWeibo ? true : false;
  123 +
  124 + co(function* () {
  125 + // let mipData = mipUtils.process(testStr, 0);
  126 + let detail = yield req.ctx(DetailModel).packageData(id, isApp, isWeixin, channel, isShare);
  127 + let data = {
  128 + guangDetail: true,
  129 + guang: {}
  130 + };
  131 +
  132 + data.guang.isWeixin = isWeixin;
  133 + data.guang.channel = channel;
  134 + data.guang.isShare = isShare;
  135 +
  136 + if (detail.code === 400) {
  137 + return next();
  138 + }
  139 + if (!detail.getArticle) {
  140 + // TODO 跳转到逛首页
  141 + return;
  142 + }
  143 +
  144 + if (isShare && detail && detail.sideNav) {
  145 + data.sideNav = detail.sideNav;
  146 + }
  147 +
  148 + // 作者信息数据
  149 + if (detail && detail.getAuthor && (typeof detail.getAuthor.name !== 'undefined')) {
  150 + data.guang.author = {
  151 + avatar: detail.getAuthor.avatar.replace('http://', '//'),
  152 + name: detail.getAuthor.name,
  153 + intro: detail.getAuthor.author_desc
  154 + };
  155 +
  156 + // guang双头部的问题 20160601
  157 + // 正确的URL
  158 + let url = `${detail.getAuthor.url}&openby:yohobuy={"action":"go.h5","params":{"param":{},"share":"","id":${detail.getAuthor.author_id},"type":0,"islogin":"N","url":"${detail.getAuthor.url}"}}`; // eslint-disable-line
  159 +
  160 + data.guang.author.url = helpers.https(url);
  161 + }
  162 + let guang = data.guang;
  163 +
  164 + guang.detail = {
  165 + id: _.get(detail, 'getArticle.id'),
  166 + title: detail.getArticle.article_title,
  167 + publishTime: detail.getArticle.publishTime,
  168 + pageView: detail.getArticle.pageViews,
  169 + content: []
  170 + };
  171 + if (!detail.getArticleContent) {
  172 + return next();
  173 + }
  174 +
  175 + let processContents = guangProcess.processArticleDetail(detail.getArticleContent,
  176 + isApp,
  177 + gender,
  178 + isWeixin,
  179 + isqq,
  180 + isWeibo, isMip);
  181 +
  182 + let goodsList = yield req.ctx(DetailModel).productInfoBySkns(processContents.allgoods);
  183 +
  184 + guang.detail.content = guangProcess.pushGoodsInfo(processContents.finalDetail, goodsList, isApp);
  185 +
  186 + // 相关品牌
  187 + if (detail.getBrand && detail.getBrand.length) {
  188 + guang.relatedBrand = _relatedBrand(detail.getBrand, isApp);
  189 + }
  190 +
  191 + // 相关标签
  192 + if (detail.getArticle.tags && detail.getArticle.tags.length) {
  193 + guang.relatedTag = _relatedTag(detail.getArticle.tags, isApp);
  194 + }
  195 +
  196 + // 相关文章
  197 + if (detail.getOtherArticle && detail.getOtherArticle.length) {
  198 + guang.relatedInfo = _relatedInfo(detail.getOtherArticle, isApp);
  199 + }
  200 +
  201 + return res.render('guang/detail', Object.assign({
  202 + css: yield css('guang/detail.css'),
  203 + gender: gender,
  204 + localStyle: processContents.css,
  205 + title: detail.getArticle.article_title
  206 + }, data));
  207 + })().catch(next);
  208 +};
  209 +
  210 +module.exports = {
  211 + detailIndex
  212 +};
  1 +const fs = require('fs');
  2 +const path = require('path');
  3 +const cssnano = require('cssnano');
  4 +
  5 +const logger = global.yoho.logger;
  6 +const css = {};
  7 +
  8 +module.exports = (file) => {
  9 + const isNotDev = process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test';
  10 +
  11 + if (css[file] && isNotDev) {
  12 + return Promise.resolve(css[file]);
  13 + }
  14 +
  15 + const cssPath = path.join(__dirname, 'css', file);
  16 + const cssfile = fs.readFileSync(cssPath).toString();
  17 +
  18 + logger.info(`cssnano process ${cssPath}`);
  19 + return cssnano.process(cssfile, {
  20 + safe: true,
  21 + autoprefixer: {
  22 + add: true,
  23 + browsers: ['> 1%', 'android >=4', 'ios >=8']
  24 + }
  25 + }).then(function(result) {
  26 + if (result && result.css) {
  27 + css[file] = result.css;
  28 + return css[file];
  29 + }
  30 + return '';
  31 + }).catch(err => {
  32 + logger.error(err);
  33 + return '';
  34 + });
  35 +};
  1 +.guang-detail-c {
  2 + max-width: 750px;
  3 + width: 100%;
  4 + overflow: hidden;
  5 + background-color: #f0f0f0;
  6 + margin:0 auto;
  7 +}
  8 +.clearfix:after {
  9 + content: '';
  10 + display: block;
  11 + clear: both;
  12 +}
  13 +.guang-detail-c .editor-info {
  14 + width: 100%;
  15 + height: 65px;
  16 + border-bottom: 1px solid #e0e0e0;
  17 + padding: 0 20px;
  18 + background-color: #fff;
  19 +}
  20 +.editor-info .pic {
  21 + border-radius: 50%;
  22 + overflow: hidden;
  23 + margin-top: 10px;
  24 + width: 45px;
  25 + height: 45px;
  26 + float: left;
  27 +}
  28 +.editor-info .name {
  29 + line-height: 65px;
  30 + float: left;
  31 + font-size: 14px;
  32 + color: #000;
  33 + padding-left: 20px;
  34 +}
  35 +.editor-info .intro {
  36 + line-height: 65px;
  37 + float: left;
  38 + font-size: 14px;
  39 + color: #b0b0b0;
  40 + padding-left: 20px;
  41 +}
  42 +.article-info {
  43 + padding: 10px 20px;
  44 + background-color: #fff;
  45 +}
  46 +.article-info .name {
  47 + line-height: 30px;
  48 + font-size: 20px;
  49 + color: #000;
  50 + font-weight: bold;
  51 +}
  52 +.article-info .view-c {
  53 + color: #b0b0b0;
  54 + font-size: 12px;
  55 + line-height: 30px;
  56 +}
  57 +.guang-content {
  58 + padding: 0 20px 20px;
  59 + background-color: #fff;
  60 + float: left;
  61 + margin-bottom: 20px;
  62 + box-sizing: border-box;
  63 + width: 100%;
  64 +}
  65 +.guang-content p {
  66 + padding: 5px 0;
  67 +}
  68 +.guang-content .pic {
  69 + clear: both;
  70 + margin: 0 auto;
  71 + display: block;
  72 + max-width: 100%;
  73 +}
  74 +.related-goods {
  75 + float: left;
  76 + width: 100%;
  77 + background-color: #fff;
  78 +}
  79 +.related-goods .good-item {
  80 + width: 100%;
  81 + border-top: 1px solid #e0e0e0;
  82 + float: left;
  83 + padding: 20px 0;
  84 +}
  85 +.related-goods .good-item .pic {
  86 + width: 25%;
  87 + float: left;
  88 +}
  89 +.pic-mip {
  90 + max-width: 100%;
  91 +}
  92 +.related-goods .good-item .info {
  93 + width: 70%;
  94 + float: left;
  95 + margin-left: 5%;
  96 +}
  97 +.related-goods .good-item .name {
  98 + color: #000;
  99 + padding: 10px 0;
  100 + font-size: 14px;
  101 + display: -webkit-box;
  102 + -webkit-line-clamp: 3;
  103 + overflow: hidden;
  104 + text-overflow: ellipsis;
  105 + -webkit-box-orient: vertical;
  106 + line-height: 25px;
  107 +}
  108 +.related-goods .good-item .price{
  109 + color: #d62927;
  110 + font-size: 14px;
  111 +}
  112 +.more-goods {
  113 + height: 40px;
  114 + line-height: 40px;
  115 + clear: both;
  116 + border-top: 1px solid #e0e0e0;
  117 + border-bottom: 1px solid #e0e0e0;
  118 + font-size: 16px;
  119 + background-color: #fff;
  120 + padding: 0 20px;
  121 + display: block;
  122 +}
  123 +.recommend-goods {
  124 + width: 100%;
  125 + overflow-x: scroll;
  126 + overflow-y: hidden;
  127 + margin: 20px 0;
  128 + background-color: #fff;
  129 + -webkit-overflow-scrolling: touch;
  130 +}
  131 +.good-scroll {
  132 + height: 133px;
  133 + margin: 20px 0;
  134 + overflow: hidden;
  135 + display: inline-flex;
  136 + padding-right: 20px;
  137 +}
  138 +.recommend-goods .good-item {
  139 + float: left;
  140 + position: relative;
  141 + width: 100px;
  142 + margin-left: 20px;
  143 +}
  144 +.recommend-goods .good-item .price{
  145 + width: 100px;
  146 + height: 20px;
  147 + color: #fff;
  148 + text-align: center;
  149 + line-height: 20px;
  150 + position: absolute;
  151 + bottom: 0;
  152 + left: 0;
  153 + background-color: #000;
  154 + opacity: 0.7;
  155 +}
  156 +.related-brand {
  157 + background-color: #fff;
  158 +}
  159 +.brand-c {
  160 + width: 100%;
  161 + float: left;
  162 + background-color: #fff;
  163 +}
  164 +.related-brand .title {
  165 + line-height: 60px;
  166 + text-align: center;
  167 + color: #000;
  168 + font-size: 18px;
  169 + background-color: #fff;
  170 +}
  171 +.brand-item {
  172 + width: 20%;
  173 + margin: 10px 2.5%;
  174 + float: left;
  175 +}
  176 +.brand-item p {
  177 + font-size: 13px;
  178 + color: #babac2;
  179 + line-height: 20px;
  180 + text-align: center;
  181 + width: 100%;
  182 + overflow: hidden;
  183 + text-overflow: ellipsis;
  184 + white-space: nowrap;
  185 +}
  186 +.brand-tag {
  187 + border-top: 1px solid #e0e0e0;
  188 + float: left;
  189 + background-color: #fff;
  190 + width: 100%;
  191 + padding-bottom: 20px;
  192 + padding-right: 20px;
  193 + box-sizing: border-box;
  194 +}
  195 +.brand-tag .tag{
  196 + background-color: #444;
  197 + float: left;
  198 + color: #fff;
  199 + line-height: 30px;
  200 + padding: 0 10px;
  201 + margin: 20px 0 0 20px;
  202 +}
  203 +.related-info {
  204 + margin: 20px 0;
  205 + padding: 20px;
  206 + background-color: #fff;
  207 + float: left;
  208 + padding-bottom: 0;
  209 +}
  210 +.related-info .info-item {
  211 + width: 100%;
  212 + float: left;
  213 + margin-bottom: 20px;
  214 +}
  215 +.related-info .info-item .pic {
  216 + width: 35%;
  217 + float: left;
  218 +}
  219 +.related-info .info-item .info {
  220 + width: 60%;
  221 + margin-left: 5%;
  222 + float: left;
  223 +}
  224 +.related-info .info-item .title{
  225 + display: -webkit-box;
  226 + -webkit-box-orient: vertical;
  227 + -webkit-line-clamp: 3;
  228 + word-break: break-all;
  229 + overflow: hidden;
  230 + line-height: 20px;
  231 +}
  232 +.related-info .info-item .time {
  233 + color: #b0b0b0;
  234 + line-height: 22px;
  235 +}
  236 +.big-pic {
  237 + position: relative;
  238 + margin-bottom: 3px;
  239 + clear: both;
  240 +}
  241 +.small-pic {
  242 + position: relative;
  243 + margin-bottom: 3px;
  244 + width: 100%;
  245 +}
  246 +.small-pic .pic {
  247 + float: left;
  248 + width: 50%;
  249 + clear: none;
  250 + max-width: 50%;
  251 +}
  252 +.tag-list-box {
  253 + width: 100%;
  254 + left: 0;
  255 + bottom: 0;
  256 + position: absolute;
  257 + padding: 10px;
  258 +}
  259 +.label-box {
  260 + clear: both;
  261 +}
  262 +.lable-info-box {
  263 + font-size: 12px;
  264 + background-color: rgba(0, 0, 0, 0.6);
  265 + color: #fff;
  266 + padding: 0 5px;
  267 + border-radius: 15px;
  268 + float: left;
  269 + line-height: 25px;
  270 + width: 60%;
  271 + height: 25px;
  272 + overflow: hidden;
  273 + text-overflow: ellipsis;
  274 + white-space: nowrap;
  275 + margin-top: 5px;
  276 +}
  277 +.lable-info-box a {
  278 + color: #fff;
  279 + overflow: hidden;
  280 + text-overflow: ellipsis;
  281 +}
  282 +.lable-focus {
  283 + float: left;
  284 + display: flex;
  285 + align-items: center;
  286 + line-height: 25px;
  287 + height: 25px;
  288 + margin-right: 15px;
  289 + margin-top: 5px;
  290 +}
  291 +.lable-focus .focus-big {
  292 + display: flex;
  293 + align-items: center;
  294 + width: 17px;
  295 + height: 17px;
  296 + border-radius: 17px;
  297 + border: 4px solid rgba(253, 157, 43, 0.5);
  298 + -webkit-box-pack: center;
  299 + -webkit-justify-content: center;
  300 + justify-content: center;
  301 +}
  302 +.lable-focus .focus-small {
  303 + width: 17px;
  304 + height: 17px;
  305 + border-radius: 17px;
  306 + background: rgba(253, 157, 43, 1);
  307 + -webkit-transform: scale(0.5, 0.5);
  308 + transform: scale(0.5, 0.5);
  309 + margin: 0;
  310 +}
  311 +
  312 +
  1 +/**
  2 + * sub app service
  3 + * @author: xuan.chen@yoho.cn<xuan.chen@yoho.cn>
  4 + * @date: 2016/11/21
  5 + */
  6 +
  7 +var express = require('express'),
  8 + path = require('path');
  9 +
  10 +var app = express();
  11 +
  12 +// set view engin
  13 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
  14 +
  15 +app.on('mount', function(parent) {
  16 + delete parent.locals.settings; // 不继承父 App 的设置
  17 + Object.assign(app.locals, parent.locals);
  18 +});
  19 +
  20 +app.use(global.yoho.hbs({
  21 + extname: '.hbs',
  22 + defaultLayout: 'mip',
  23 + layoutsDir: doraemon,
  24 + partialsDir: [path.join(__dirname, 'views/partial')],
  25 + views: path.join(__dirname, 'views/action'),
  26 + helpers: global.yoho.helpers
  27 +}));
  28 +
  29 +app.locals.layout = 'mip';
  30 +
  31 +// router
  32 +app.use(require('./router'));
  33 +
  34 +module.exports = app;
  1 +const cheerio = require('cheerio');
  2 +
  3 +module.exports = {
  4 + /**
  5 + * 分离 CSS
  6 + * @param {*} $
  7 + * @param {*} prefix
  8 + */
  9 + processStyle($, prefix) {
  10 + let css = [];
  11 +
  12 + $('*').each(function(index) {
  13 + let $this = $(this);
  14 + let localStyle = $this.attr('style');
  15 + let className = `yoho-inline-style-${prefix}-${index}`;
  16 +
  17 + if (localStyle) {
  18 + css.push(`.${className}{${localStyle}}`);
  19 + $this.removeAttr('style');
  20 + $this.addClass(className);
  21 + }
  22 + });
  23 + return css.join('');
  24 + },
  25 +
  26 + /**
  27 + * 替换标签
  28 + * @param {*} $
  29 + */
  30 + processTag($) {
  31 + let css = [];
  32 +
  33 + // mip-anim
  34 + $('img[src*=".gif"]').each(function() {
  35 + let $this = $(this);
  36 + let mipAnim = `<mip-anim layout="responsive" width="350" height="263" src="${$this.attr('src')}" alt="${$this.attr('alt') || ''}" class="${$this.attr('class') || ''}"></mip-anim>`; // eslint-disable-line
  37 +
  38 + $this.replaceWith(mipAnim);
  39 + });
  40 +
  41 + // mip-img
  42 + $('img').each(function() {
  43 + let $this = $(this);
  44 + let mipImg = `<mip-img layout="responsive" width="350" height="263" src="${$this.attr('src')}" alt="${$this.attr('alt') || ''}" class="${$this.attr('class') || ''}"></mip-img>`; // eslint-disable-line
  45 +
  46 + $this.replaceWith(mipImg);
  47 + });
  48 +
  49 + // mip-video
  50 + $('video').each(function() {
  51 + let $this = $(this);
  52 + let mipVideo = `<mip-video layout="responsive" width="350" height="263" poster="${$this.attr('poster') || ''}" src="${$this.attr('src')}" alt="${$this.attr('alt') || ''}" class="${$this.attr('class') || ''}"></mip-video>`; // eslint-disable-line
  53 +
  54 + $this.replaceWith(mipVideo);
  55 + });
  56 +
  57 + // mip-audio
  58 + $('audio').each(function() {
  59 + let $this = $(this);
  60 + let mipAudio = `<mip-audio src="${$this.attr('src')}"></mip-audio>`; // eslint-disable-line
  61 +
  62 + $this.replaceWith(mipAudio);
  63 + });
  64 +
  65 + // mip-iframe
  66 + $('iframe').each(function() {
  67 + let $this = $(this);
  68 + let mipIframe = `<mip-iframe layout="responsive" width="350" height="263" src="${$this.attr('src')}" class="${$this.attr('class') || ''}"></mip-iframe>`; // eslint-disable-line
  69 +
  70 + $this.replaceWith(mipIframe);
  71 + });
  72 +
  73 + // mip-link
  74 + $('a').each(function() {
  75 + let $this = $(this);
  76 + let mipLink = `<mip-link href="${$this.attr('href')}" class="${$this.attr('class') || ''}" title="${$this.attr('title') || ''}">${$this.html()}</mip-link>`; // eslint-disable-line
  77 +
  78 + $this.replaceWith(mipLink);
  79 + });
  80 +
  81 + // style
  82 + $('style').each(function() {
  83 + let $this = $(this);
  84 +
  85 + css.push($this.html());
  86 + $this.remove();
  87 + });
  88 +
  89 + return {
  90 + mipHtml: $.html(),
  91 + css: css.join('')
  92 + };
  93 + },
  94 +
  95 + /**
  96 + * 处理 HTML 为 MIP 所需要的格式
  97 + * @param {*} html HTML
  98 + * @param {*} prefix CSS 前缀
  99 + */
  100 +
  101 + process(html = '', prefix = 0) {
  102 + let $ = cheerio.load(html, {
  103 + decodeEntities: false
  104 + });
  105 + let inlineStyle = this.processStyle($, prefix); // 必须先处理内联样式
  106 + let tagHtml = this.processTag($);
  107 +
  108 + return {
  109 + mipHtml: tagHtml.mipHtml,
  110 + css: tagHtml.css + inlineStyle
  111 + };
  112 + }
  113 +};
  114 +
  1 +/**
  2 + * 逛详情models
  3 + * @author: chenfeng<feng.chen@yoho.cn>
  4 + * @date: 2016/09/07
  5 + */
  6 +'use strict';
  7 +const serviceAPI = global.yoho.ServiceAPI;
  8 +const api = global.yoho.API;
  9 +const _ = require('lodash');
  10 +const helpers = global.yoho.helpers;
  11 +
  12 +const URI_PACKAGE_ARTICLE = 'guang/service/v2/article/';
  13 +const URI_PACKAGE_AUTHOR = 'guang/service/v1/author/';
  14 +
  15 +
  16 +class DetailModel extends global.yoho.BaseModel {
  17 + constructor(ctx) {
  18 + super(ctx);
  19 + }
  20 +
  21 + /**
  22 + * 获取二级菜单顶部颜色
  23 + * @param {[string]} choosed
  24 + * @return {[string]}
  25 + */
  26 + _getSidebarColor(choosed) {
  27 + let color = false;
  28 +
  29 + if (choosed === 'girls') {
  30 + color = '#FF88AE';
  31 + } else if (choosed === 'kids') {
  32 + color = '#7ad9f9';
  33 + } else if (choosed === 'lifestyle') {
  34 + color = '#4f4138';
  35 + }
  36 +
  37 + return color;
  38 + }
  39 +
  40 + /**
  41 + * 微信侧边栏导航数据
  42 + * @param {*} list
  43 + * @param {*} choosed
  44 + */
  45 + _processSideBar(list, choosed) {
  46 + const formatData = [];
  47 + let offset = 0; // 分割数组用到的游标
  48 +
  49 + list = list || [];
  50 +
  51 + _.forEach(list, (item, i) => {
  52 + if (item.sub) {
  53 + item.sub.unshift({
  54 + sort_name: item.sort_name,
  55 + sort_name_en: item.sort_name_en,
  56 + back: true,
  57 + isSelect: false,
  58 + bgColor: this._getSidebarColor(choosed)
  59 + });
  60 + }
  61 +
  62 + // 如果有分隔符,分割数组
  63 + if (item.separative_sign === 'Y') {
  64 + formatData.push(list.slice(offset, i));
  65 + offset = i;
  66 + }
  67 + });
  68 +
  69 + // 数组被分割剩余的部分
  70 + formatData.push(list.slice(offset));
  71 + return formatData;
  72 + }
  73 +
  74 + _getLeftNav(choosed) {
  75 + choosed = choosed || 'all';
  76 +
  77 + return serviceAPI.get('operations/api/v6/category/getCategory', {}, {
  78 + cache: true
  79 + }).then(result => {
  80 + if (result && result.code === 200) {
  81 +
  82 + return this._processSideBar(result.data, choosed);
  83 + }
  84 + });
  85 + }
  86 +
  87 + _getShareData(id) {
  88 + return serviceAPI.get('guang/api/v6/share/guang', {
  89 + id: id
  90 + }).then(result => {
  91 + if (result && result.code === 200) {
  92 +
  93 + return result.data;
  94 + }
  95 + });
  96 + }
  97 +
  98 + /**
  99 + * 获取文章接口调用
  100 + * @param {*} articleId
  101 + */
  102 + _getArticle(articleId) {
  103 + return serviceAPI.get(`${URI_PACKAGE_ARTICLE}getArticle`, {
  104 + article_id: articleId
  105 + }, {
  106 + cache: true
  107 + });
  108 + }
  109 +
  110 + /**
  111 + * 获取作者接口调用
  112 + * @param {*} authorId
  113 + */
  114 + _getAuthor(authorId) {
  115 + return serviceAPI.get(`${URI_PACKAGE_AUTHOR}getAuthor`, {
  116 + author_id: authorId
  117 + }, {
  118 + cache: true
  119 + });
  120 + }
  121 +
  122 + /**
  123 + * 获取文章详情接口调用
  124 + * @param {*} articleId
  125 + */
  126 + _getArticleContent(articleId) {
  127 + return serviceAPI.get(`${URI_PACKAGE_ARTICLE}getArticleContent`, {
  128 + article_id: articleId
  129 + }, {
  130 + cache: true
  131 + });
  132 + }
  133 +
  134 + /**
  135 + * 获取文章相关品牌接口调用
  136 + * @param {*} articleId
  137 + */
  138 + _getBrand(articleId) {
  139 + return serviceAPI.get(`${URI_PACKAGE_ARTICLE}getBrand`, {
  140 + article_id: articleId
  141 + }, {
  142 + cache: true
  143 + });
  144 + }
  145 +
  146 + /**
  147 + * 获取资讯相关的其它资讯接口调用
  148 + * @param {*} articleId
  149 + * @param {*} tag
  150 + */
  151 + _getOtherArticle(articleId, tag) {
  152 + return serviceAPI.get(`${URI_PACKAGE_ARTICLE}getOtherArticle`, {
  153 + article_id: articleId,
  154 + tags: tag,
  155 + offset: 0,
  156 + limit: 3
  157 + }, {
  158 + cache: true
  159 + });
  160 + }
  161 +
  162 + /**
  163 + * APP 获取微信模块
  164 + */
  165 + _getSingleTemplateWechat() {
  166 + return api.get('', {
  167 + method: 'app.resources.getSingleTemplate',
  168 + module: 'wechat',
  169 + key: 'guang_detail_wechat'
  170 + });
  171 + }
  172 +
  173 + /**
  174 + * [逛资讯详情页数据封装]
  175 + * @param {[int]} id [内容ID]
  176 + * @param {Boolean} isApp [标识是否是APP访问]
  177 + * @return {[array]}
  178 + */
  179 + packageData(id, isApp, isWeixin, channel, isShare) {
  180 + let result = {
  181 + getAuthor: {},
  182 + getArticle: {},
  183 + getArticleContent: {},
  184 + getBrand: {},
  185 + getOtherArticle: {}
  186 + };
  187 +
  188 + // 获取资讯
  189 + return this._getArticle(id).then(data => {
  190 + // 调用接口失败
  191 + if (!data || data.code !== 200) {
  192 + result.code = 400;
  193 + return result;
  194 + }
  195 + let article = result.getArticle = data && data.data || {};
  196 + let promises = [
  197 + this._getAuthor(article.author_id),
  198 + this._getArticleContent(id),
  199 + this._getBrand(id)
  200 + ];
  201 +
  202 + // 获取资讯相关的其它资讯
  203 + if (typeof article.tag !== 'undefined') {
  204 + promises.push(this._getOtherArticle(id, article.tag));
  205 + }
  206 +
  207 + // APP 获取微信模块
  208 + if (isApp) {
  209 + promises.push(this._getSingleTemplateWechat());
  210 + }
  211 +
  212 + if (isShare) {
  213 + let navGender = _.cloneDeep(channel);
  214 +
  215 + promises.push(
  216 + this._getLeftNav(navGender),
  217 + this._getShareData(id)
  218 + );
  219 + }
  220 +
  221 + return Promise.all(promises).then(datas => {
  222 + let getArticleContent = [];
  223 +
  224 + if (!datas) {
  225 + return result;
  226 + }
  227 +
  228 + if (datas[1]) {
  229 + result.getArticleContent = getArticleContent = datas[1].data;
  230 + }
  231 +
  232 + if (isApp && datas[4] && datas[4].data) {
  233 + let preCount = 0;
  234 + let i;
  235 +
  236 + for (i = 0; i < getArticleContent.length; i++) {
  237 + if (getArticleContent[i].singleImage ||
  238 + getArticleContent[i].text || getArticleContent[i].smallPic) {
  239 + preCount++;
  240 + }
  241 + }
  242 +
  243 + _.forEach(datas[4].data, item => {
  244 + item.src = helpers.image(item.src, 280, 210, 1);
  245 + });
  246 +
  247 + getArticleContent.splice(preCount, 0, {
  248 + weixinPublic: datas[4].data
  249 + });
  250 + }
  251 +
  252 + if (isShare && datas[5]) {
  253 + if (datas[5].wechatShareImgUrl) {
  254 + datas[5].wechatShareImgUrl =
  255 + datas[5].wechatShareImgUrl.substring(datas[5].wechatShareImgUrl.indexOf('//'));
  256 +
  257 + if (datas[5].wechatShareImgUrl.indexOf('?') === -1) {
  258 + datas[5].wechatShareImgUrl = datas[5].wechatShareImgUrl +
  259 + '?imageView2/2/interlace/1/q/75';
  260 + }
  261 + } else if (datas[5].qqShareImgUrl) {
  262 + datas[5].qqShareImgUrl =
  263 + datas[5].qqShareImgUrl.substring(datas[5].qqShareImgUrl.indexOf('//'));
  264 +
  265 + if (datas[5].qqShareImgUrl.indexOf('?') === -1) {
  266 + datas[5].qqShareImgUrl = datas[5].wechatShareImgUrl + '?imageView2/2/interlace/1/q/75';
  267 + }
  268 + } else if (datas[5].showShareImgUrl) {
  269 + datas[5].showShareImgUrl =
  270 + datas[5].showShareImgUrl.substring(datas[5].showShareImgUrl.indexOf('//'));
  271 +
  272 + if (datas[5].showShareImgUrl.indexOf('?') === -1) {
  273 + datas[5].showShareImgUrl = datas[5].showShareImgUrl + '?imageView2/2/interlace/1/q/75';
  274 + }
  275 + }
  276 +
  277 + let preCount = 0;
  278 + let i;
  279 +
  280 + for (i = 0; i < getArticleContent.length; i++) {
  281 + if (getArticleContent[i].singleImage ||
  282 + getArticleContent[i].text || getArticleContent[i].smallPic) {
  283 + preCount = i + 1;
  284 + }
  285 + }
  286 +
  287 + getArticleContent.splice(preCount, 0, {
  288 + shareCode: datas[5]
  289 + });
  290 + }
  291 +
  292 + if (datas[0]) {
  293 + result.getAuthor = datas[0].data;
  294 + }
  295 +
  296 +
  297 + if (datas[2]) {
  298 + result.getBrand = datas[2].data;
  299 + }
  300 +
  301 + if (isShare && datas[4]) {
  302 + result.sideNav = datas[4];
  303 + }
  304 +
  305 + if (datas.length === 5 && isApp || datas.length === 4 && !isApp || datas.length === 6 && isShare) {
  306 + if (datas[3]) {
  307 + result.getOtherArticle = datas[3].data;
  308 + }
  309 + }
  310 +
  311 + return result;
  312 + });
  313 + });
  314 + }
  315 +
  316 + /**
  317 + * [获取详情信息]
  318 + * @param {[int]} id [资讯id]
  319 + * @return {[object]}
  320 + */
  321 + intro(id) {
  322 + let param = {
  323 + article_id: id,
  324 + client_type: 'h5'
  325 + };
  326 +
  327 + return serviceAPI.get(`${URI_PACKAGE_ARTICLE}getArticleContent`, param, {
  328 + cache: true
  329 + });
  330 + }
  331 +
  332 + /**
  333 + * [根据商品SKN获取商品的简要信息]
  334 + * @param {[array]} sknString [skns]
  335 + * @return {[type]}
  336 + */
  337 + productInfoBySkns(sknString) {
  338 + // 调用搜索接口
  339 + let param = {
  340 + method: 'h5.product.batch',
  341 + productSkn: sknString,
  342 + order: 's_t_desc'
  343 + };
  344 +
  345 + return api.get('', param, {
  346 + cache: true
  347 + }).then(result => {
  348 + return _.get(result, 'data.product_list', []);
  349 + });
  350 + }
  351 +}
  352 +
  353 +module.exports = DetailModel;
  1 +/**
  2 + * router of sub app service
  3 + * @author: xuan.chen@yoho.cn<xuan.chen@yoho.cn>
  4 + * @date: 2016/11/21
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const router = require('express').Router(); //eslint-disable-line
  10 +const cRoot = './controllers';
  11 +
  12 +const rewrite = require('../../doraemon/middleware/rewrite');
  13 +const mip = require('../../doraemon/middleware/mip');
  14 +const guang = require(`${cRoot}/guang`);
  15 +
  16 +router.use(mip);
  17 +
  18 +router.get(/^\/guang\/info\/(.*?)\.html/, rewrite.resolve, guang.detailIndex);
  19 +
  20 +module.exports = router;
  1 +<div class="guang-detail-c">
  2 + {{# guang}}
  3 + {{# author}}
  4 + <div class="editor-info" data-id={{id}}>
  5 + <mip-link href="{{url}}">
  6 + <mip-img class="pic" width="45" height="45" src={{image2 avatar mode=2 q=60}}>
  7 + </mip-img>
  8 + <div class="name">{{name}}</div>
  9 + <div class="intro">{{intro}}</div>
  10 + </mip-link>
  11 + </div>
  12 + {{/ author}}
  13 + {{# detail}}
  14 + <div class="article-info">
  15 + <p class="name">{{title}}</p>
  16 + <div class="view-c">
  17 + <span>日期:{{publishTime}}</span>
  18 + </div>
  19 + </div>
  20 + <div class="guang-content clearfix">
  21 + {{# content}}
  22 + {{#if text}}
  23 + {{{text}}}
  24 + {{/if}}
  25 + {{#if bigImage}}
  26 + <div class="big-pic">
  27 + {{#if isGif}}
  28 + <mip-anim class="pic" width="{{width}}" height="{{height}}" src="{{image2 bigImage q=60}}"></mip-anim>
  29 + {{else}}
  30 + <mip-img class="pic" width="{{width}}" height="{{height}}" src="{{image2 bigImage q=60}}"></mip-img>
  31 + {{/if}}
  32 + {{#if tagList}}
  33 + <div class="tag-list-box">
  34 + {{#each tagList}}
  35 + {{#unless isApp}}
  36 + <div class="label-box" data-skn="{{product_skn}}">
  37 + <div class="lable-focus">
  38 + <div class="focus-big">
  39 + <div class="focus-small"></div>
  40 + </div>
  41 + </div>
  42 + <div class="lable-info-box{{#if isApp}} lable-infobox-borderadius{{/if}}"><mip-link href="{{href}}">{{tagName}}</mip-link></div>
  43 + <div class="lable-btn add-to-cart" data-skn="{{product_skn}}">
  44 + </div>
  45 + </div>
  46 + {{/unless}}
  47 + {{/each}}
  48 + </div>
  49 + {{/if}}
  50 + </div>
  51 + {{/if}}
  52 + {{#if smallImage}}
  53 + <div class="small-pic">
  54 + {{# smallImage}}
  55 + {{#if isGif}}
  56 + <mip-anim class="pic" width="160" height="160" src="{{image2 src q=60}}"></mip-anim>
  57 + {{else}}
  58 + <mip-img class="pic" width="160" height="160" src="{{image2 src q=60}}"></mip-img>
  59 + {{/if}}
  60 + {{/ smallImage}}
  61 + </div>
  62 + {{/if}}
  63 + {{#if relatedReco}}
  64 + <div class="related-goods clearfix">
  65 + {{#each relatedReco.goods}}
  66 + {{#if url}}
  67 + <div class="good-item">
  68 + <mip-link href="{{url}}">
  69 + {{#if default_images}}
  70 + <div class="pic">
  71 + <mip-img class="pic-mip" width="76" height="102" src="{{image2 default_images w=152 h=204}}">
  72 + </mip-img>
  73 + </div>
  74 + {{/if}}
  75 + <div class="info">
  76 + <p class="name">{{product_name}}</p>
  77 + {{#if sales_price}}
  78 + <p class="price">&yen;{{sales_price}}</p>
  79 + {{/if}}
  80 + </div>
  81 + </mip-link>
  82 + </div>
  83 + {{/if}}
  84 + {{/each}}
  85 + </div>
  86 + {{/if}}
  87 + {{/ content}}
  88 + </div>
  89 + {{/ detail}}
  90 + {{#detail.content}}
  91 + {{#if moreLink}}
  92 + <mip-link class="more-goods" href="{{moreLink}}">更多商品</mip-link>
  93 + {{/if}}
  94 + {{#if recommendProducts}}
  95 + <div class="recommend-goods">
  96 + <div class="good-scroll">
  97 + {{#each recommendProducts}}
  98 + <div class="good-item">
  99 + <mip-link href="{{href}}">
  100 + <mip-img class="pic" width="100" height="134" src="{{image2 pic_url w=152 h=204}}">
  101 + </mip-img>
  102 + </mip-link>
  103 + <p class="price">&yen;{{price}}</p>
  104 + </div>
  105 + {{/each}}
  106 + </div>
  107 + </div>
  108 + {{/if}}
  109 + {{/detail.content}}
  110 + {{#if relatedBrand}}
  111 + <div class="related-brand clearfix">
  112 + <div class="title">相关品牌</div>
  113 + <div class="brand-c">
  114 + {{# relatedBrand}}
  115 + <div class="brand-item">
  116 + <mip-link href="{{url}}">
  117 + <div class="pic">
  118 + <mip-img class="pic-mip" width="75" height="75" src="{{thumb}}">
  119 + </mip-img>
  120 + </div>
  121 + <p>{{name}}</p>
  122 + </mip-link>
  123 + </div>
  124 + {{/ relatedBrand}}
  125 + </div>
  126 + </div>
  127 + {{/if}}
  128 + {{#if relatedInfo}}
  129 + <div class="related-info">
  130 + {{# relatedInfo}}
  131 + <div class="info-item">
  132 + <mip-link href="{{url}}">
  133 + <div class="pic">
  134 + <mip-img class="pic-mip" width="117" height="73" src="{{thumb}}">
  135 + </mip-img>
  136 + </div>
  137 + <div class="info">
  138 + <p class="title">{{title}}</p>
  139 + <p class="time">{{publishTime}}</p>
  140 + </div>
  141 + </mip-link>
  142 + </div>
  143 + {{/ relatedInfo}}
  144 + </div>
  145 + {{/if}}
  146 + {{#if relatedTag}}
  147 + <div class="brand-tag">
  148 + {{# relatedTag}}
  149 + <mip-link href="//guang.m.yohobuy.com/tags/index?query={{name}}" class="tag">{{name}}</mip-link>
  150 + {{/ relatedTag}}
  151 + </div>
  152 + {{/if}}
  153 + {{/ guang}}
  154 +</div>
@@ -14,6 +14,7 @@ const crypto = global.yoho.crypto; @@ -14,6 +14,7 @@ const crypto = global.yoho.crypto;
14 const helpers = global.yoho.helpers; 14 const helpers = global.yoho.helpers;
15 const productProcess = require(`${utils}/product-process`); 15 const productProcess = require(`${utils}/product-process`);
16 const searchModel = require(`${mRoot}/search`); 16 const searchModel = require(`${mRoot}/search`);
  17 +const searchProcess = require(`${utils}/search-process`);
17 18
18 /** 19 /**
19 * 从 useragent 获取 uid 20 * 从 useragent 获取 uid
@@ -236,7 +237,7 @@ const category = (req, res, next) => { @@ -236,7 +237,7 @@ const category = (req, res, next) => {
236 } 237 }
237 238
238 searchModel.getSearchData(initialData).then((firstPageGoods) => { 239 searchModel.getSearchData(initialData).then((firstPageGoods) => {
239 - res.render('search/goods-list', { 240 + res.render('search/goods-list', Object.assign({
240 _noLazy: true, // 首屏不使用lazyload 241 _noLazy: true, // 首屏不使用lazyload
241 module: 'product', 242 module: 'product',
242 page: 'search-list', 243 page: 'search-list',
@@ -252,7 +253,7 @@ const category = (req, res, next) => { @@ -252,7 +253,7 @@ const category = (req, res, next) => {
252 localCss: true, 253 localCss: true,
253 appPath: appPath, 254 appPath: appPath,
254 introText: req.query.intro_text 255 introText: req.query.intro_text
255 - }); 256 + }, searchProcess.getListSeoData(req.query.gender, req.query.title || req.query.sort_name)));
256 }).catch(next); 257 }).catch(next);
257 }; 258 };
258 259
@@ -51,7 +51,13 @@ const newDetail = { @@ -51,7 +51,13 @@ const newDetail = {
51 pageHeader: headerData, 51 pageHeader: headerData,
52 result: result, 52 result: result,
53 page: 'new-detail', 53 page: 'new-detail',
54 - title: result.goodsName, 54 + title: result.goodsName + result.sortName + '正品 | YOHO!BUY 有货',
  55 + keywords: result.brandName + result.sortName + ',' + result.brandName + '官网专卖店,' +
  56 + result.brandName + '官方授权店,' + result.brandName + '正品,' + result.brandName + '打折,' +
  57 + result.brandName + '折扣店,' +
  58 + result.brandName + '真品,' + result.brandName + '代购',
  59 + description: `YOHO!BUY 有货-${result.brandName}官方授权店,${result.goodsName}图片、报价、介绍。` +
  60 + `YOHO!BUY 有货${result.brandName}官网专卖店提供${result.brandName}正品、${result.brandName}真品、 ${result.brandName}打折、${result.brandName}代购等。`, // eslint-disable-line
55 pageFooter: true, 61 pageFooter: true,
56 localCss: true, 62 localCss: true,
57 appPath: appPath 63 appPath: appPath
@@ -431,6 +431,10 @@ const shop = { @@ -431,6 +431,10 @@ const shop = {
431 let decorators = redShopPrcs.pushGoodsInfo(decoratorsAll.decorators, goodsList); 431 let decorators = redShopPrcs.pushGoodsInfo(decoratorsAll.decorators, goodsList);
432 432
433 res.render('newshop/shop-reds', { 433 res.render('newshop/shop-reds', {
  434 + title: shopInfo.shop_name + '|' + shopInfo.shop_name + '潮流服装服饰-Yoho!Buy有货',
  435 + keywords: shopInfo.shop_name + ',' + shopInfo.shop_name + '服装服饰,' + shopInfo.shop_name + '潮流服装服饰',
  436 + description: shopInfo.shop_name + '|Yoho!Buy有货' + shopInfo.shop_name +
  437 + '潮流服饰官方授权店!100%品牌正品保证,支持货到付款。',
434 pageHeader: _.assign({ 438 pageHeader: _.assign({
435 shopPage: { 439 shopPage: {
436 text: '分类', 440 text: '分类',
@@ -182,7 +182,7 @@ let discountDetail = (req, res, next) => { @@ -182,7 +182,7 @@ let discountDetail = (req, res, next) => {
182 params.renderData.pageHeader.navTitle = result.title; 182 params.renderData.pageHeader.navTitle = result.title;
183 183
184 // 唤起 APP 的路径 184 // 唤起 APP 的路径
185 - res.locals.appPath = `yohobuy://yohobuy.com/goapp?openby:yohobuy={"action":"go.discountmarketpro","params":{"id":"${id}","cover_url":"${result.activity.cover_url.split('?')[0]}","title":"${result.title}"}}`; 185 + res.locals.appPath = result.activity && result.activity.cover_url ? `yohobuy://yohobuy.com/goapp?openby:yohobuy={"action":"go.discountmarketpro","params":{"id":"${id}","cover_url":"${result.activity.cover_url.split('?')[0]}","title":"${result.title}"}}` : '';
186 186
187 res.render('sale/discount-detail', Object.assign(params.renderData, result, { 187 res.render('sale/discount-detail', Object.assign(params.renderData, result, {
188 localCss: true 188 localCss: true
@@ -174,7 +174,7 @@ const index = (req, res, next) => { @@ -174,7 +174,7 @@ const index = (req, res, next) => {
174 } 174 }
175 175
176 })((result) => { 176 })((result) => {
177 - res.render('search/index', { 177 + res.render('search/index', Object.assign({
178 module: 'product', 178 module: 'product',
179 page: 'search-index', 179 page: 'search-index',
180 pageHeader: headerModel.setNav({ 180 pageHeader: headerModel.setNav({
@@ -190,7 +190,7 @@ const index = (req, res, next) => { @@ -190,7 +190,7 @@ const index = (req, res, next) => {
190 wantTerms: result.guessTerms 190 wantTerms: result.guessTerms
191 } 191 }
192 192
193 - }); 193 + }, searchProcess.getListSeoData()));
194 }); 194 });
195 }; 195 };
196 196
@@ -132,6 +132,8 @@ const tool = { @@ -132,6 +132,8 @@ const tool = {
132 132
133 // 底部简介URL链接 133 // 底部简介URL链接
134 dest.introUrl = '/product/detail/intro/' + origin.product_skn; 134 dest.introUrl = '/product/detail/intro/' + origin.product_skn;
  135 + dest.brandName = _.get(origin, 'brand_info.brand_name', '');
  136 + dest.sortName = _.get(origin, 'middle_sort_name', '');
135 137
136 return dest; 138 return dest;
137 }, 139 },
@@ -30,4 +30,7 @@ module.exports = app => { @@ -30,4 +30,7 @@ module.exports = app => {
30 30
31 // 用户服务: 在线客服 31 // 用户服务: 在线客服
32 app.use('/service', require('./apps/service')); 32 app.use('/service', require('./apps/service'));
  33 +
  34 + // MIP
  35 + app.use('/mip', require('./apps/mip'));
33 }; 36 };
  1 +'use strict';
  2 +
  3 +module.exports = (req, res, next) => {
  4 + Object.assign(res.locals, {
  5 + canonical: 'https://m.yohobuy.com' + req.path,
  6 + miphtml: 'https://m.yohobuy.com/mip' + req.originalUrl
  7 + });
  8 +
  9 + next();
  10 +};
@@ -25,56 +25,66 @@ const seoMap = { @@ -25,56 +25,66 @@ const seoMap = {
25 keywords: '创意生活,创意生活馆,潮流家居,潮流创意家居,家居生活用品,Yoho!Buy有货', 25 keywords: '创意生活,创意生活馆,潮流家居,潮流创意家居,家居生活用品,Yoho!Buy有货',
26 description: 'Yoho!BUY有货官网创意生活频道提供了潮流创意家居,家居生活用品等正品网购,给您的生活带来更多创意。' 26 description: 'Yoho!BUY有货官网创意生活频道提供了潮流创意家居,家居生活用品等正品网购,给您的生活带来更多创意。'
27 }, 27 },
28 - '/product/sale?channel=boys': { 28 + '/product/boys-sale/': {
29 title: '折扣男装专区|男装SALE折扣,男款鞋包配饰特卖|Yoho!Buy有货 100%正品保证', 29 title: '折扣男装专区|男装SALE折扣,男款鞋包配饰特卖|Yoho!Buy有货 100%正品保证',
30 keywords: 'SALE,男装SALE,男装折扣,男款鞋包配饰特卖', 30 keywords: 'SALE,男装SALE,男装折扣,男款鞋包配饰特卖',
31 description: 'Yoho!Buy有货SALE频道提供男装折扣,精致品牌男装sale,限时特惠。Yoho!Buy有货男装折扣,100%正品保证!' 31 description: 'Yoho!Buy有货SALE频道提供男装折扣,精致品牌男装sale,限时特惠。Yoho!Buy有货男装折扣,100%正品保证!'
32 }, 32 },
33 - '/product/sale?gender=2,3': { 33 + '/product/girls-sale/': {
34 title: '折扣女装专区|女装SALE折扣,女款鞋包配饰特卖|Yoho!Buy有货', 34 title: '折扣女装专区|女装SALE折扣,女款鞋包配饰特卖|Yoho!Buy有货',
35 keywords: 'SALE,女装SALE,女装折扣,女款鞋包配饰特卖', 35 keywords: 'SALE,女装SALE,女装折扣,女款鞋包配饰特卖',
36 description: 'Yoho!Buy有货SALE频道提供女装折扣,精致品牌女装sale,限时特惠。Yoho!Buy有货女装折扣,100%正品保证!' 36 description: 'Yoho!Buy有货SALE频道提供女装折扣,精致品牌女装sale,限时特惠。Yoho!Buy有货女装折扣,100%正品保证!'
37 }, 37 },
38 - '/product/new?gender=1,3': { 38 + '/product/kids-sale/': {
  39 + title: 'SALE|童装SALE,童装折扣,潮童鞋包配饰特卖|YOHO!BUY有货',
  40 + keywords: 'SALE,童装SALE,童装折扣,潮童鞋包配饰特卖',
  41 + description: 'YOHO!BUY有货SALE频道提供童装折扣,精致品牌童装sale,童装,针织衫,外套 卫衣,夹克,棉衣,裤子,品牌童鞋,潮童鞋包配饰等限时特惠。YOHO!BUY有货潮童折扣,100%正品保证!'
  42 + },
  43 + '/product/lifestyle-sale/': {
  44 + title: 'SALE|家居生活用品SALE,生活用品折扣,数码家居特卖YOHO!BUY有货',
  45 + keywords: 'SALE,家居生活用品SALE,生活用品折扣,数码家居特卖',
  46 + description: 'YOHO!BUY有货SALE频道提供生活用品折扣,数码家居特卖,数码3c,居家,玩具娱乐,文具,美妆等限时特惠。YOHO!BUY有货家居生活用品SALE,,100%正品保证! '
  47 + },
  48 + '/product/boys-new/': {
39 title: '男生潮装新品|男装新品发布,饰品推荐|Yoho!Buy有货 ', 49 title: '男生潮装新品|男装新品发布,饰品推荐|Yoho!Buy有货 ',
40 keywords: '男生潮装新品,新品发布,新品男装,新款男装推荐,新款男鞋推荐,新款男包推荐,新款男饰品推荐,Yoho!Buy有货', 50 keywords: '男生潮装新品,新品发布,新品男装,新款男装推荐,新款男鞋推荐,新款男包推荐,新款男饰品推荐,Yoho!Buy有货',
41 description: 'Yoho!Buy有货男装新品到着为您提供新品男装,男装新品直达就选Yoho!Buy有货,100%正品保证!' 51 description: 'Yoho!Buy有货男装新品到着为您提供新品男装,男装新品直达就选Yoho!Buy有货,100%正品保证!'
42 }, 52 },
43 - '/product/new?gender=2,3': { 53 + '/product/girls-new/': {
44 title: '女生潮装新品|女装新品发布,饰品推荐|Yoho!Buy有货 ', 54 title: '女生潮装新品|女装新品发布,饰品推荐|Yoho!Buy有货 ',
45 keywords: '女生潮流新品,女款新品发布,新品女装,新款女装推荐,新款女鞋推荐,新款女包推荐,新款饰品推荐,Yoho!Buy有货', 55 keywords: '女生潮流新品,女款新品发布,新品女装,新款女装推荐,新款女鞋推荐,新款女包推荐,新款饰品推荐,Yoho!Buy有货',
46 description: 'Yoho!Buy有货女装新品到着为您提供新品女装,女装样品,女装新款推荐;汇集国内外最新款女装,鞋,女包,饰品,100%正品保证!' 56 description: 'Yoho!Buy有货女装新品到着为您提供新品女装,女装样品,女装新款推荐;汇集国内外最新款女装,鞋,女包,饰品,100%正品保证!'
47 }, 57 },
48 - '/product/new?msort=365&channel=4': { 58 + '/product/kids-new/': {
49 title: '新品到着|潮童新品发布,新款童装童鞋,包包配饰推荐|Yoho!Buy有货', 59 title: '新品到着|潮童新品发布,新款童装童鞋,包包配饰推荐|Yoho!Buy有货',
50 keywords: '潮童新品发布,新品童装,新款童装推荐,新款童鞋,新款儿童鞋包,儿童配饰新品,Yoho!Buy有货', 60 keywords: '潮童新品发布,新品童装,新款童装推荐,新款童鞋,新款儿童鞋包,儿童配饰新品,Yoho!Buy有货',
51 description: 'Yoho!Buy有货潮童新品到着为您提供新品童装,童装样品,童装新款推荐;汇集国内外最新款童装,童鞋,儿童鞋包配饰。' 61 description: 'Yoho!Buy有货潮童新品到着为您提供新品童装,童装样品,童装新款推荐;汇集国内外最新款童装,童鞋,儿童鞋包配饰。'
52 }, 62 },
53 - '/product/new?msort=10&channel=4': { 63 + '/product/lifestyle-new/': {
54 title: '新品到着|数码3c,居家,玩具娱乐,文具,美妆|Yoho!Buy有货', 64 title: '新品到着|数码3c,居家,玩具娱乐,文具,美妆|Yoho!Buy有货',
55 keywords: '数码3c,居家,玩具娱乐,文具,美妆,Yoho!Buy有货', 65 keywords: '数码3c,居家,玩具娱乐,文具,美妆,Yoho!Buy有货',
56 description: 'Yoho!Buy有货创意生活新品到着为您提供潮流创意生活,汇集国内外最新款数码3c,居家,玩具娱乐,文具,美妆。' 66 description: 'Yoho!Buy有货创意生活新品到着为您提供潮流创意生活,汇集国内外最新款数码3c,居家,玩具娱乐,文具,美妆。'
57 }, 67 },
58 - '/brands?channel=1': { 68 + '/boys-brands/': {
59 title: '潮流男装品牌|男装品牌排行榜,男装品牌大全|Yoho!Buy有货', 69 title: '潮流男装品牌|男装品牌排行榜,男装品牌大全|Yoho!Buy有货',
60 keywords: '潮流男装品牌,男装品牌,男装品牌排行榜,男装品牌大全,Yoho!Buy有货', 70 keywords: '潮流男装品牌,男装品牌,男装品牌排行榜,男装品牌大全,Yoho!Buy有货',
61 description: 'Yoho!Buy有货男装品牌一览汇集国内国际各大男装品牌大全,为广大爱好时尚的男士青年提供品牌男装、休闲男装、商务男装.Yoho!Buy有货,100%正品保证' 71 description: 'Yoho!Buy有货男装品牌一览汇集国内国际各大男装品牌大全,为广大爱好时尚的男士青年提供品牌男装、休闲男装、商务男装.Yoho!Buy有货,100%正品保证'
62 }, 72 },
63 - '/brands?channel=2': { 73 + '/girls-brands/': {
64 title: '潮流女装品牌|女装品牌排行榜,女装品牌大全|Yoho!Buy有货', 74 title: '潮流女装品牌|女装品牌排行榜,女装品牌大全|Yoho!Buy有货',
65 keywords: '潮流女装品牌,女装品牌,女装品牌排行榜,女装品牌大全,Yoho!Buy有货', 75 keywords: '潮流女装品牌,女装品牌,女装品牌排行榜,女装品牌大全,Yoho!Buy有货',
66 description: 'Yoho!Buy有货女装品牌一览汇集各大女装品牌,提供品牌女装、休闲女装、商务女装.Yoho!Buy有货品牌女装100%正品保证。' 76 description: 'Yoho!Buy有货女装品牌一览汇集各大女装品牌,提供品牌女装、休闲女装、商务女装.Yoho!Buy有货品牌女装100%正品保证。'
67 }, 77 },
68 - '/brands?channel=3': { 78 + '/kids-brands/': {
69 title: '品牌一览|童装童鞋品牌,儿童鞋包配饰排行榜大全|Yoho!Buy有货', 79 title: '品牌一览|童装童鞋品牌,儿童鞋包配饰排行榜大全|Yoho!Buy有货',
70 keywords: '童装品牌,童装童鞋排行榜,儿童鞋包配饰排行榜,潮童品牌大全,品牌一览,Yoho!Buy有货', 80 keywords: '童装品牌,童装童鞋排行榜,儿童鞋包配饰排行榜,潮童品牌大全,品牌一览,Yoho!Buy有货',
71 description: 'Yoho!Buy有货童装品牌一览汇集国内国际各大童装品牌大全,提供品牌童装、童鞋,儿童鞋包配饰,100%正品保证' 81 description: 'Yoho!Buy有货童装品牌一览汇集国内国际各大童装品牌大全,提供品牌童装、童鞋,儿童鞋包配饰,100%正品保证'
72 }, 82 },
73 - '/brands?channel=4': { 83 + '/lifestyle-brands/': {
74 title: '品牌一览|数码3c,居家,玩具娱乐,文具,美妆品牌|Yoho!Buy有货', 84 title: '品牌一览|数码3c,居家,玩具娱乐,文具,美妆品牌|Yoho!Buy有货',
75 keywords: '数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,美妆品牌', 85 keywords: '数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,美妆品牌',
76 description: 'Yoho!Buy有货女装品牌一览汇集国内国际各大数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,美妆品牌.' 86 description: 'Yoho!Buy有货女装品牌一览汇集国内国际各大数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,美妆品牌.'
77 - }, 87 + }
78 /* eslint-enable */ 88 /* eslint-enable */
79 }; 89 };
80 90
@@ -12,8 +12,11 @@ @@ -12,8 +12,11 @@
12 <meta content="email=no" name="format-detection" /> 12 <meta content="email=no" name="format-detection" />
13 <meta name="referrer" content="always"> 13 <meta name="referrer" content="always">
14 {{# cononical}} 14 {{# cononical}}
15 - <link rel="cononical" href="{{currentHref}}"/> 15 + <link rel="cononical" href="{{currentHref}}">
16 {{/ cononical}} 16 {{/ cononical}}
  17 + {{#if miphtml}}
  18 + <link rel="miphtml" href="{{miphtml}}">
  19 + {{/if}}
17 {{#dnsPrefetch.hosts}} 20 {{#dnsPrefetch.hosts}}
18 <link rel="dns-prefetch" href="{{this}}"> 21 <link rel="dns-prefetch" href="{{this}}">
19 {{/dnsPrefetch.hosts}} 22 {{/dnsPrefetch.hosts}}
@@ -23,7 +26,7 @@ @@ -23,7 +26,7 @@
23 var isWechat = /micromessenger/i.test(navigator.userAgent || ''); 26 var isWechat = /micromessenger/i.test(navigator.userAgent || '');
24 if (isWechat) { 27 if (isWechat) {
25 document.title =document.title.replace(' | Yoho!Buy有货 | 潮流购物逛不停', ''); 28 document.title =document.title.replace(' | Yoho!Buy有货 | 潮流购物逛不停', '');
26 - (function () { if (typeof (WeixinJSBridge) == "undefined") { document.addEventListener("WeixinJSBridgeReady", function (a) { setTimeout(function () { WeixinJSBridge.invoke("setFontSizeCallback", { fontSize: 0 }, function (b) { }) }, 0) }) } else { setTimeout(function () { WeixinJSBridge.invoke("setFontSizeCallback", { fontSize: 0 }, function (a) { }) }, 0) } })(); 29 + (function(){function setWechatSize(){if(typeof WeixinJSBridge!=="undefined"&&WeixinJSBridge.invoke){WeixinJSBridge.invoke("setFontSizeCallback",{fontSize:0},function(){})}}if(typeof WeixinJSBridge!=="undefined"){setTimeout(setWechatSize,0)}else{document.addEventListener("WeixinJSBridgeReady",function(){setTimeout(setWechatSize,0)})};}());
27 } 30 }
28 </script> 31 </script>
29 32
  1 +<!DOCTYPE html>
  2 +<html mip>
  3 +<head>
  4 + <meta charset="utf-8">
  5 + <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
  6 + <title>{{title}} | Yoho!Buy有货 | 潮流购物逛不停</title>
  7 + <link rel="stylesheet" type="text/css" href="https://mipcache.bdstatic.com/static/v1/mip.css">
  8 + <link rel="canonical" href="{{canonical}}">
  9 + <style mip-custom>
  10 + .main-wrap{width: 100%;max-width: 750px;margin: 0 auto;}
  11 + {{css}}
  12 + {{localStyle}}
  13 + </style>
  14 +</head>
  15 +<body>
  16 +<div class="main-wrap">{{{body}}}</div>
  17 +<mip-stats-baidu token="d22478778b220ee60bce74bd15d390ae" setconfig="%5B'_trackEvent'%2C%20'mip'%2C%20'{{canonical}}'%5D"></mip-stats-baidu>
  18 +<script src="https://mipcache.bdstatic.com/static/v1/mip.js"></script>
  19 +<script src="https://mipcache.bdstatic.com/static/v1/mip-stats-baidu/mip-stats-baidu.js"></script>
  20 +<script src="https://mipcache.bdstatic.com/static/v1/mip-anim/mip-anim.js"></script>
  21 +<script src="https://mipcache.bdstatic.com/static/v1/mip-link/mip-link.js"></script>
  22 +<script src="https://mipcache.bdstatic.com/static/v1/mip-audio/mip-audio.js"></script>
  23 +</body>
  24 +</html>
@@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
12 a.async = 1; 12 a.async = 1;
13 a.src = j; 13 a.src = j;
14 m.parentNode.insertBefore(a, m); 14 m.parentNode.insertBefore(a, m);
15 - }(window, document, 'script', (document.location.protocol === 'https:' ? 'https:' : 'http:') + '//cdn.yoho.cn/yas-jssdk/2.4.0/yas.js', '_yas')); 15 + }(window, document, 'script', (document.location.protocol === 'https:' ? 'https:' : 'http:') + '//cdn.yoho.cn/yas-jssdk/2.4.1/yas.js', '_yas'));
16 16
17 var _hmt = _hmt || []; 17 var _hmt = _hmt || [];
18 18
@@ -56,7 +56,7 @@ @@ -56,7 +56,7 @@
56 uid = uid === 0 ? '' : uid; 56 uid = uid === 0 ? '' : uid;
57 window._ozuid = uid; // 暴露ozuid 57 window._ozuid = uid; // 暴露ozuid
58 if (window._yas) { 58 if (window._yas) {
59 - window._yas(1 * new Date(), '2.4.0', 'yohobuy_m', uid, '', ''); 59 + window._yas(1 * new Date(), '2.4.1', 'yohobuy_m', uid, '', '');
60 } 60 }
61 61
62 setTimeout(function() { 62 setTimeout(function() {
@@ -75,7 +75,7 @@ @@ -75,7 +75,7 @@
75 }, 1000); 75 }, 1000);
76 }()); 76 }());
77 77
78 - /* tar add 170426 品众代码去除 */ 78 + {{!--/* tar add 170426 品众代码去除 */--}}
79 {{!--window._fxcmd = window._fxcmd || []; 79 {{!--window._fxcmd = window._fxcmd || [];
80 _fxcmd.sid = 'bb3b16fa1106a6ab8619da0095755f32'; 80 _fxcmd.sid = 'bb3b16fa1106a6ab8619da0095755f32';
81 _fxcmd.trackAll = false; 81 _fxcmd.trackAll = false;
@@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
90 var sc = document.getElementsByTagName('script')[0]; 90 var sc = document.getElementsByTagName('script')[0];
91 sc.parentNode.insertBefore(_pzfx,sc); 91 sc.parentNode.insertBefore(_pzfx,sc);
92 }, 1000);--}} 92 }, 1000);--}}
93 - 93 +
94 94
95 </script> 95 </script>
96 96
1 { 1 {
2 "name": "m-yohobuy-node", 2 "name": "m-yohobuy-node",
3 - "version": "5.7.1", 3 + "version": "5.7.3",
4 "private": true, 4 "private": true,
5 "description": "A New Yohobuy Project With Express", 5 "description": "A New Yohobuy Project With Express",
6 "repository": { 6 "repository": {
@@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
26 "body-parser": "^1.16.1", 26 "body-parser": "^1.16.1",
27 "captchapng": "0.0.1", 27 "captchapng": "0.0.1",
28 "cheerio": "^0.22.0", 28 "cheerio": "^0.22.0",
  29 + "cssnano": "^3.10.0",
29 "client-sessions": "^0.8.0", 30 "client-sessions": "^0.8.0",
30 "compression": "^1.6.2", 31 "compression": "^1.6.2",
31 "connect-memcached": "^0.2.0", 32 "connect-memcached": "^0.2.0",
@@ -52,7 +53,7 @@ @@ -52,7 +53,7 @@
52 "xml2js": "^0.4.17", 53 "xml2js": "^0.4.17",
53 "yoho-express-session": "^2.0.0", 54 "yoho-express-session": "^2.0.0",
54 "yoho-md5": "^2.0.0", 55 "yoho-md5": "^2.0.0",
55 - "yoho-node-lib": "=0.2.25", 56 + "yoho-node-lib": "=0.2.27",
56 "yoho-zookeeper": "^1.0.8" 57 "yoho-zookeeper": "^1.0.8"
57 }, 58 },
58 "devDependencies": { 59 "devDependencies": {
@@ -62,7 +63,6 @@ @@ -62,7 +63,6 @@
62 "babel-polyfill": "^6.23.0", 63 "babel-polyfill": "^6.23.0",
63 "babel-preset-env": "^1.4.0", 64 "babel-preset-env": "^1.4.0",
64 "css-loader": "^0.28.1", 65 "css-loader": "^0.28.1",
65 - "cssnano": "^3.10.0",  
66 "eslint": "^3.19.0", 66 "eslint": "^3.19.0",
67 "eslint-config-yoho": "^1.0.1", 67 "eslint-config-yoho": "^1.0.1",
68 "eslint-loader": "^1.7.1", 68 "eslint-loader": "^1.7.1",
@@ -103,7 +103,7 @@ @@ -103,7 +103,7 @@
103 "yoho-fastclick": "^1.0.6", 103 "yoho-fastclick": "^1.0.6",
104 "yoho-hammer": "^2.0.7", 104 "yoho-hammer": "^2.0.7",
105 "yoho-iscroll": "^5.2.0", 105 "yoho-iscroll": "^5.2.0",
106 - "yoho-jquery": "^2.2.4", 106 + "yoho-jquery": "^1.12.4",
107 "yoho-jquery-lazyload": "^1.9.12", 107 "yoho-jquery-lazyload": "^1.9.12",
108 "yoho-jquery-qrcode": "^0.14.0", 108 "yoho-jquery-qrcode": "^0.14.0",
109 "yoho-mlellipsis": "0.0.3", 109 "yoho-mlellipsis": "0.0.3",
@@ -306,7 +306,7 @@ plusstar = { @@ -306,7 +306,7 @@ plusstar = {
306 }); 306 });
307 }, 300); 307 }, 300);
308 }, 308 },
309 - goodsList: function() { 309 + goodsList: function(reload) {
310 let that = this, 310 let that = this,
311 skn = []; 311 skn = [];
312 312
@@ -319,6 +319,10 @@ plusstar = { @@ -319,6 +319,10 @@ plusstar = {
319 return false; 319 return false;
320 } 320 }
321 321
  322 + if (!reload) {
  323 + $('.goods').append('<div class="divide">正在加载...</div>');
  324 + }
  325 +
322 loading.showLoadingMask(); 326 loading.showLoadingMask();
323 skn = that.common.productSkns.slice((that.common.page - 1) * 327 skn = that.common.productSkns.slice((that.common.page - 1) *
324 that.common.pagesize, that.common.page * that.common.pagesize); 328 that.common.pagesize, that.common.page * that.common.pagesize);
@@ -356,6 +360,7 @@ plusstar = { @@ -356,6 +360,7 @@ plusstar = {
356 PAGE_NUM: that.common.page - 1 360 PAGE_NUM: that.common.page - 1
357 }); 361 });
358 362
  363 + $('.divide').remove();
359 $('.plusstar-resources .goods').append(data); 364 $('.plusstar-resources .goods').append(data);
360 365
361 $('.plusstar-resources .goods').find('img.lazy:not([src])').lazyload(); 366 $('.plusstar-resources .goods').find('img.lazy:not([src])').lazyload();
@@ -399,6 +404,8 @@ $(function() { @@ -399,6 +404,8 @@ $(function() {
399 404
400 plusstar.init(); 405 plusstar.init();
401 406
  407 + plusstar.goodsList(true);
  408 +
402 // 滚动翻页 409 // 滚动翻页
403 $(window).scroll(function() { 410 $(window).scroll(function() {
404 scrollFn(); 411 scrollFn();
@@ -15,13 +15,13 @@ let fCbFn, hCbFn; // 筛选和关闭的回调 @@ -15,13 +15,13 @@ let fCbFn, hCbFn; // 筛选和关闭的回调
15 // 隐藏筛选界面 15 // 隐藏筛选界面
16 function hideFilter() { 16 function hideFilter() {
17 setTimeout(function() { 17 setTimeout(function() {
18 - $filter.addClass('hide'); 18 + $filter && $filter.addClass('hide');
19 }, 301); 19 }, 301);
20 } 20 }
21 21
22 // 显示筛选界面 22 // 显示筛选界面
23 function showFilter() { 23 function showFilter() {
24 - $filter.removeClass('hide'); 24 + $filter && $filter.removeClass('hide');
25 } 25 }
26 26
27 // 一级菜单点击时背景高亮 27 // 一级菜单点击时背景高亮
@@ -356,6 +356,10 @@ function filterInit() { @@ -356,6 +356,10 @@ function filterInit() {
356 url: '/product/sale/filter', 356 url: '/product/sale/filter',
357 data: defaultOpt, 357 data: defaultOpt,
358 success: function(data) { 358 success: function(data) {
  359 + if (data === '') {
  360 + return false;
  361 + }
  362 +
359 $goodsContainer.append(data); 363 $goodsContainer.append(data);
360 364
361 // 初始化filter&注册filter回调 365 // 初始化filter&注册filter回调
@@ -198,6 +198,15 @@ @@ -198,6 +198,15 @@
198 .goods { 198 .goods {
199 background-color: #fff; 199 background-color: #fff;
200 padding: 0 14px; 200 padding: 0 14px;
  201 +
  202 + .divide {
  203 + float: left;
  204 + height: 50px;
  205 + width: 100%;
  206 + padding: 10px 0;
  207 + color: #ccc;
  208 + text-align: center;
  209 + }
201 } 210 }
202 } 211 }
203 212
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 const _ = require('lodash'); 6 const _ = require('lodash');
7 const helpers = global.yoho.helpers; 7 const helpers = global.yoho.helpers;
8 const productPrcs = require('./product-process'); 8 const productPrcs = require('./product-process');
  9 +const mipUtils = require('../apps/mip/mip-utils');
9 10
10 /** 11 /**
11 * 将商品转化成以 product_skn 为键名的对象 12 * 将商品转化成以 product_skn 为键名的对象
@@ -103,7 +104,7 @@ const formatArticle = (articleData, showTag, isApp, showAuthor, uid) => { @@ -103,7 +104,7 @@ const formatArticle = (articleData, showTag, isApp, showAuthor, uid) => {
103 }; 104 };
104 let originUrl = helpers.urlFormat(colparam.urlpath || '/author/index', null, 'guang'); // 跳转回的链接 105 let originUrl = helpers.urlFormat(colparam.urlpath || '/author/index', null, 'guang'); // 跳转回的链接
105 // 根据用户是否登录做处理的链接 106 // 根据用户是否登录做处理的链接
106 - let collectUrl = 'javascript:;'; // eslint-disable-line 107 + let collectUrl = 'javascript:;'; // eslint-disable-line
107 108
108 if (!uid) { 109 if (!uid) {
109 let playUrlEncode = `${originUrl}${colparam.param}`.replace(/\//g, '\\\/'); 110 let playUrlEncode = `${originUrl}${colparam.param}`.replace(/\//g, '\\\/');
@@ -206,16 +207,27 @@ const getProductIcon = (type) => { @@ -206,16 +207,27 @@ const getProductIcon = (type) => {
206 /** 207 /**
207 * 逛详情页数据处理 208 * 逛详情页数据处理
208 */ 209 */
209 -const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isWeibo) => { 210 +const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isWeibo, isMip) => {
210 let finalDetail = []; 211 let finalDetail = [];
211 let allgoods = ''; 212 let allgoods = '';
  213 + let localStyle = [];
212 214
213 _.forEach(articleContent, (value, index) => { 215 _.forEach(articleContent, (value, index) => {
214 216
215 // 文字 217 // 文字
216 if (_.get(value, 'text.data.text', false)) { 218 if (_.get(value, 'text.data.text', false)) {
  219 + let newText = '';
  220 +
  221 + if (isMip) {
  222 + let mipHtml = mipUtils.process(value.text.data.text, index);
  223 +
  224 + newText = mipHtml.mipHtml;
  225 + localStyle.push(mipHtml.css);
  226 + } else {
  227 + newText = value.text.data.text;
  228 + }
217 finalDetail.push({ 229 finalDetail.push({
218 - text: value.text.data.text 230 + text: newText
219 }); 231 });
220 } 232 }
221 233
@@ -235,7 +247,10 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW @@ -235,7 +247,10 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW
235 finalDetail.push({ 247 finalDetail.push({
236 bigImage: helpers.image(_.get(value, 'singleImage.data[0].src', ''), 640, 640), 248 bigImage: helpers.image(_.get(value, 'singleImage.data[0].src', ''), 640, 640),
237 noLazy: index <= 3, 249 noLazy: index <= 3,
238 - tagList: tagList 250 + tagList: tagList,
  251 + isGif: /\.gif/i.test(_.get(value, 'singleImage.data[0].src', '')),
  252 + width: 320,
  253 + height: _.get(value, 'singleImage.data[0].height', 0) / _.get(value, 'singleImage.data[0].width', 1) * 320 // eslint-disable-line
239 }); 254 });
240 } 255 }
241 256
@@ -244,12 +259,14 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW @@ -244,12 +259,14 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW
244 finalDetail.push({ 259 finalDetail.push({
245 smallImage: [ 260 smallImage: [
246 { 261 {
247 - src: helpers.image(_.get(value, 'smallPic.data[0].src', ''), 315, 420) 262 + src: helpers.image(_.get(value, 'smallPic.data[0].src', ''), 315, 420),
  263 + isGif: /\.gif/i.test(_.get(value, 'smallPic.data[0].src', '')),
248 }, 264 },
249 { 265 {
250 - src: helpers.image(_.get(value, 'smallPic.data[1].src', ''), 315, 420) 266 + src: helpers.image(_.get(value, 'smallPic.data[1].src', ''), 315, 420),
  267 + isGif: /\.gif/i.test(_.get(value, 'smallPic.data[0].src', '')),
251 } 268 }
252 - ], 269 + ]
253 }); 270 });
254 } 271 }
255 272
@@ -296,8 +313,7 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW @@ -296,8 +313,7 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW
296 finalDetail.push({ 313 finalDetail.push({
297 relatedReco: { 314 relatedReco: {
298 isApp: isApp, 315 isApp: isApp,
299 - goods: goodsData,  
300 - moreNum: goodsData.length - 2 > 0 ? goodsData.length - 2 : 0 316 + goods: goodsData
301 } 317 }
302 }); 318 });
303 } 319 }
@@ -350,7 +366,8 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW @@ -350,7 +366,8 @@ const processArticleDetail = (articleContent, isApp, gender, isWeixin, isqq, isW
350 366
351 return { 367 return {
352 finalDetail: finalDetail, 368 finalDetail: finalDetail,
353 - allgoods: allgoods 369 + allgoods: allgoods,
  370 + css: localStyle.join('')
354 }; 371 };
355 }; 372 };
356 373
@@ -362,9 +379,26 @@ const pushGoodsInfo = (finalDetail, goodsList, isApp) => { @@ -362,9 +379,26 @@ const pushGoodsInfo = (finalDetail, goodsList, isApp) => {
362 379
363 _.forEach(finalDetail, (value, key) => { 380 _.forEach(finalDetail, (value, key) => {
364 if (value.relatedReco) { 381 if (value.relatedReco) {
365 - _.forEach(value.relatedReco.goods, (item, subKey) => {  
366 - finalDetail[key].relatedReco.goods[subKey] = goodsObj[item.id]; 382 + let goodsIds = [];
  383 +
  384 + _.forEach(value.relatedReco.goods, relatedGoods => {
  385 + goodsIds.push(relatedGoods.id);
  386 + });
  387 +
  388 + goodsIds = _.uniq(goodsIds);
  389 + finalDetail[key].relatedReco.goods = [];
  390 +
  391 + _.forEach(goodsIds, (item, subKey) => {
  392 + if (goodsObj[item]) {
  393 + finalDetail[key].relatedReco.goods[subKey] = goodsObj[item];
  394 + } else {
  395 + delete finalDetail[key].relatedReco.goods[subKey];
  396 + }
367 }); 397 });
  398 +
  399 + let moreNum = _.get(finalDetail[key], 'relatedReco.goods.length', 0);
  400 +
  401 + finalDetail[key].relatedReco.moreNum = moreNum - 2 > 0 ? moreNum - 2 : 0;
368 } 402 }
369 403
370 if (value.collocation) { 404 if (value.collocation) {
@@ -143,6 +143,24 @@ module.exports = { @@ -143,6 +143,24 @@ module.exports = {
143 return ''; 143 return '';
144 } 144 }
145 }, 145 },
  146 +
  147 + /**
  148 + * 图片质量调整
  149 + */
  150 + imageslim: function(imageUrl) {
  151 + if (imageUrl && _.isString(imageUrl)) {
  152 + let urls = imageUrl.split('?');
  153 + let uri = urls[0];
  154 +
  155 + if (uri.indexOf('http:') === 0) {
  156 + uri = uri.replace('http:', '');
  157 + }
  158 +
  159 + return uri + '?imageslim';
  160 + } else {
  161 + return '';
  162 + }
  163 + },
146 isEqualOr: function() { 164 isEqualOr: function() {
147 let args = Array.prototype.slice.call(arguments); 165 let args = Array.prototype.slice.call(arguments);
148 let v1 = args[0]; 166 let v1 = args[0];
@@ -4,6 +4,12 @@ @@ -4,6 +4,12 @@
4 * @date: 2016/7/29 4 * @date: 2016/7/29
5 */ 5 */
6 6
  7 +const _getGender = {
  8 + '1,3': '男生',
  9 + '2,3': '女生',
  10 + '1,2,3': '',
  11 +};
  12 +
7 /** 13 /**
8 * 根据频道判断出性别 14 * 根据频道判断出性别
9 */ 15 */
@@ -66,8 +72,31 @@ const getTypeCont = (type, order) => { @@ -66,8 +72,31 @@ const getTypeCont = (type, order) => {
66 } 72 }
67 }; 73 };
68 74
  75 +/**
  76 + * 品类列表页 SEO 数据
  77 + * @param {*} gender
  78 + * @param {*} sort_name
  79 + */
  80 +const getListSeoData = (gender, sort_name) => {
  81 + let seoData = {
  82 + title: '潮流服装配饰,创意生活用品_男生|女生|潮童服装,鞋履,配饰品牌正品-YOHO!BUY有货',
  83 + keywords: '潮流服装配饰,创意生活用品,男生服装配饰,女生服装配饰,潮童服装配饰',
  84 + description: '潮流服装配饰及创意生活正品网购!YOHO!BUY有货提供男生、女生、潮童服装配饰。100%品牌正品保证,支持货到付款。'
  85 + };
  86 +
  87 + if (gender && sort_name) {
  88 + seoData = {
  89 + title: `${sort_name}|新款${sort_name}${_getGender[gender]}|品牌正品|YOHO!BUY有货`,
  90 + keywords: `新款${sort_name},${_getGender[gender]}${sort_name},品牌正品`,
  91 + description: `正品网购!YOHO!BUY有货提供新款${sort_name}${_getGender[gender]}${sort_name}100%品牌正品保证,支持货到付款。`
  92 + };
  93 + }
  94 + return seoData;
  95 +};
  96 +
69 module.exports = { 97 module.exports = {
70 getGenderByChannel, 98 getGenderByChannel,
71 getChannelType, 99 getChannelType,
72 - getTypeCont 100 + getTypeCont,
  101 + getListSeoData
73 }; 102 };