Showing 80 changed files with 4624 additions and 1146 deletions
  1 +/**
  2 + * 领券中心 controller
  3 + * @author: shenzm<zhimin.shen@yoho.cn>
  4 + * @date: 2016/09/29
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const couponModel = require('../models/coupon');
  10 +
  11 +exports.index = (req, res, next) => {
  12 + const channel = req.cookies._Channel || 'boys';
  13 +
  14 + couponModel.getCouponData(channel, {
  15 + uid: req.user.uid,
  16 + contentCode: req.query.contentCode
  17 + }).then(result => {
  18 + res.render('coupon', Object.assign({
  19 + module: 'activity',
  20 + page: 'coupon'
  21 + }, result));
  22 + }).catch(next);
  23 +};
  24 +
  25 +exports.getCouponStatus = (req, res, next) => {
  26 + couponModel.getCouponStatus({
  27 + uid: req.user.uid,
  28 + contentCode: req.query.contentCode
  29 + }).then(result => {
  30 + res.json(result);
  31 + }).catch(next);
  32 +};
  33 +
  34 +exports.sendcoupon = (req, res, next) => {
  35 + couponModel.sendcoupon(req.query.id, req.user.uid).then(result => {
  36 + res.json(result);
  37 + }).catch(next);
  38 +};
  1 +/**
  2 + * activity model
  3 + * @author: shenzm<zhimin.shen@yoho.cn>
  4 + * @date: 2016/09/29
  5 + */
  6 +'use strict';
  7 +
  8 +const Promise = require('bluebird');
  9 +const api = global.yoho.API;
  10 +const crypto = global.yoho.crypto;
  11 +const helpers = global.yoho.helpers;
  12 +const HeaderModel = require('../../../doraemon/models/header');
  13 +const homeService = require('../../product/models/home-service');
  14 +
  15 +exports.getCouponData = (channel, params) => {
  16 + return Promise.coroutine(function*() {
  17 + const result = {
  18 + pathNav: [homeService.getHomeChannelNav(channel), {
  19 + name: '领券频道'
  20 + }],
  21 + footerTop: true,
  22 + topBanner: {
  23 + list: []
  24 + },
  25 + categories: []
  26 + };
  27 +
  28 + const requestData = yield Promise.all([
  29 + api.get('', Object.assign(params, {
  30 + method: 'app.promotion.queryCouponCenter'
  31 + })),
  32 + HeaderModel.requestHeaderData(channel)
  33 + ]);
  34 +
  35 + const coupon = requestData[0];
  36 +
  37 + result.headerData = requestData[1].headerData;
  38 +
  39 + do {
  40 + if (!coupon.data || !Array.isArray(coupon.data) || coupon.data.length === 0) {
  41 + break;
  42 + }
  43 +
  44 + let i = 0;
  45 +
  46 + coupon.data.forEach(function(val, index) {
  47 + // 头部banner
  48 + if (val.templateName === 'focus') {
  49 + val.data.forEach(function(item) {
  50 + result.topBanner.list.push({
  51 + href: item.url.replace('http:', ''), // banner跳转链接
  52 + img: item.src // banner图片
  53 + });
  54 + });
  55 + } else if (val.template_name === 'getCoupon' && val.data.length) {
  56 + // 优惠券楼层
  57 + if (!coupon.data[index - 1].data || !coupon.data[index - 1].data.text) {
  58 + return;
  59 + }
  60 +
  61 + const obj = {
  62 + title: coupon.data[index - 1].data.text, // 楼层标题
  63 + coupons: []
  64 + };
  65 +
  66 + val.data.forEach(function(item) {
  67 + obj.coupons.push({
  68 + id: crypto.encryption('yoho9646abcdefgh', item.couponID), // 加密优惠券号
  69 + img: helpers.image(item.image.src, 0, 0), // 优惠券图片
  70 + url: item.image.url.replace('http:', '') // 去逛逛链接
  71 + });
  72 + });
  73 +
  74 + result.categories.push(obj);
  75 + i++;
  76 + }
  77 + });
  78 + }
  79 + while (false);
  80 + return result;
  81 + })();
  82 +};
  83 +
  84 +exports.getCouponStatus = (params) => {
  85 + return Promise.coroutine(function*() {
  86 + const coupon = yield api.get('', Object.assign(params, {
  87 + method: 'app.promotion.queryCouponCenter'
  88 + }));
  89 + const result = {
  90 + code: coupon.code,
  91 + categories: []
  92 + };
  93 +
  94 + do {
  95 + if (!coupon.data || !Array.isArray(coupon.data) || coupon.data.length === 0) {
  96 + break;
  97 + }
  98 +
  99 + let i = 0;
  100 +
  101 + coupon.data.forEach(function(val, index) {
  102 + if (val.template_name === 'getCoupon' && val.data.length) {
  103 + // 优惠券楼层
  104 + if (!coupon.data[index - 1].data || !coupon.data[index - 1].data.text) {
  105 + return;
  106 + }
  107 +
  108 + val.data.forEach(function(item) {
  109 + const status = Number(item.status);
  110 +
  111 + if ([2, 3].indexOf(status) > -1) {
  112 + const cou = {
  113 + id: crypto.encryption('yoho9646abcdefgh', item.couponID) // 加密优惠券号
  114 + };
  115 +
  116 + if (status === 2) {
  117 + cou.empty = true; // 优惠券已抢光
  118 + } else if (status === 3) {
  119 + cou.got = true; // 优惠券已领取
  120 + }
  121 +
  122 + result.categories.push(cou);
  123 + }
  124 + });
  125 + i++;
  126 + }
  127 + });
  128 + }
  129 + while (false);
  130 + return result;
  131 + })();
  132 +};
  133 +
  134 +exports.sendcoupon = (couponId, uid) => {
  135 + let returnData = {};
  136 +
  137 + couponId = crypto.decrypt('yoho9646abcdefgh', couponId);
  138 +
  139 + // 领取优惠券
  140 + return api.get('', {
  141 + method: 'app.promotion.getCoupon',
  142 + couponId: couponId,
  143 + uid: uid
  144 + }).then(result => {
  145 + switch (result.code) {
  146 + case 200:
  147 + returnData = {
  148 + code: 200,
  149 + message: '恭喜您,成功领取优惠券',
  150 + url: helpers.urlFormat('/home/coupons')
  151 + };
  152 + break;
  153 + case 401:
  154 + returnData = {
  155 + code: 401,
  156 + message: '您已领取过优惠券'
  157 + };
  158 + break;
  159 + case 315:
  160 + returnData = {
  161 + code: 315,
  162 + message: '优惠券已过期'
  163 + };
  164 + break;
  165 + case 300:
  166 + returnData = {
  167 + code: 300,
  168 + message: '请求参数错误'
  169 + };
  170 + break;
  171 + default:
  172 + returnData = {
  173 + code: 500,
  174 + message: '领券失败!'
  175 + };
  176 + break;
  177 + }
  178 + return returnData;
  179 + });
  180 +};
@@ -9,10 +9,17 @@ @@ -9,10 +9,17 @@
9 const express = require('express'); 9 const express = require('express');
10 const router = express.Router(); // eslint-disable-line 10 const router = express.Router(); // eslint-disable-line
11 const cRoot = './controllers'; 11 const cRoot = './controllers';
  12 +const auth = require(`${global.middleware}/auth`);
12 13
13 const specialController = require(`${cRoot}/special`); 14 const specialController = require(`${cRoot}/special`);
  15 +const coupon = require(`${cRoot}/coupon`);
14 16
15 // 专题活动 17 // 专题活动
16 router.get(/^\/special\/(\d+)_(.*)\.html$/, specialController.special); 18 router.get(/^\/special\/(\d+)_(.*)\.html$/, specialController.special);
17 19
18 -module.exports = router; 20 +// 领券中心
  21 +router.get('/coupon/index', coupon.index);
  22 +router.get('/coupon/couponstatus', coupon.getCouponStatus);
  23 +router.get('/coupon/sendcoupon', auth, coupon.sendcoupon);
  24 +
  25 +module.exports = router;
  1 +<div class="coupon-page yoho-page">
  2 + {{> common/path-nav}}
  3 + {{> common/slide-banner}}
  4 +{{# categories}}
  5 + <div class="title clearfix">
  6 + <span>{{title}}</span>
  7 + </div>
  8 + {{# coupons}}
  9 + <div class="coupon">
  10 + <a href="{{url}}" target="_blank" href="javascript:void(0);" data-id="{{id}}">
  11 + <img src="{{img}}">
  12 + {{#if empty}}
  13 + <div class="coupon-mask"></div>
  14 + {{/if}}
  15 + <div class="enable info" id="{{id}}">
  16 + <div class="normal">
  17 + <p>点击</p>
  18 + <p>领取</p>
  19 + </div>
  20 + <div class="got hidden">
  21 + <p>已领取</p>
  22 + <p class="guang">去使用</p>
  23 + </div>
  24 + <div class="empty hidden">
  25 + <p>已抢光</p>
  26 + <p class="guang">去逛逛</p>
  27 + </div>
  28 + </div>
  29 + </a>
  30 + </div>
  31 + {{/ coupons}}
  32 +{{/ categories}}
  33 +
  34 +</div>
  1 +/**
  2 + * 品牌一览 controller
  3 + * @author: ghw<hongwei.gao@yoho.cn>
  4 + * @date: 2016/9/29
  5 + */
  6 +
  7 +'use strict';
  8 +const mRoot = '../models';
  9 +
  10 +const brandsService = require(`${mRoot}/brands-service`); // students model
  11 +
  12 +/**
  13 + * brands 首页
  14 + * @param {[type]} req [description]
  15 + * @param {[type]} res [description]
  16 + * @return {[type]} [description]
  17 + */
  18 +
  19 +exports.index = (req, res, next) => {
  20 + let channel = req.query.channel || req.cookies._Channel || 'boys';
  21 +
  22 + brandsService.getBrandViewList(channel, req).then(result => {
  23 +
  24 + res.render('brands/brands', result);
  25 +
  26 + }).catch(next);
  27 +};
  28 +
  29 +/**
  30 + * brandList-Ajax调用
  31 + */
  32 +exports.brandList = (req, res, next) => {
  33 + let channel = req.query.channel || req.cookies._Channel || 'boys';
  34 +
  35 + brandsService.getBrandList(channel, req.body.start).then(result => {
  36 +
  37 + res.render('brands/brand-list', Object.assign({layout: false}, result));
  38 +
  39 + }).catch(next);
  40 +};
  41 +
  42 +/**
  43 + * 品牌接口数据
  44 + *
  45 + * @param string brandId 获取品牌ID
  46 + * @return json
  47 + */
  48 +exports.brandInfo = (req, res, next) => {
  49 +
  50 + let brandId = req.query.brandId || 0;
  51 +
  52 + brandsService.brandInfo(brandId, req.user.uid).then(result => {
  53 + res.json(result);
  54 + }).catch(next);
  55 +};
  56 +
  57 +/**
  58 + * 品牌plusstar列表
  59 + */
  60 +exports.plusstarList = (req, res, next) => {
  61 + let channel = req.query.channel || req.cookies._Channel || 'boys';
  62 +
  63 + brandsService.plusstarList(channel, req).then(result => {
  64 + res.render('brands/plusstar', result);
  65 + }).catch(next);
  66 +};
  1 +/**
  2 + * router of sub app brands
  3 + * @author: ghw<hongwei.gao@yoho.cn>
  4 + * @date: 2016/09/29
  5 + */
  6 +
  7 +var express = require('express'),
  8 + path = require('path'),
  9 + hbs = require('express-handlebars');
  10 +
  11 +var app = express();
  12 +
  13 +// set view engin
  14 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
  15 +
  16 +app.on('mount', function(parent) {
  17 + delete parent.locals.settings; // 不继承父 App 的设置
  18 + Object.assign(app.locals, parent.locals);
  19 +});
  20 +
  21 +app.set('views', path.join(__dirname, 'views/action'));
  22 +app.engine('.hbs', hbs({
  23 + extname: '.hbs',
  24 + defaultLayout: 'layout',
  25 + layoutsDir: doraemon,
  26 + partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
  27 + helpers: global.yoho.helpers
  28 +}));
  29 +
  30 +// router
  31 +app.use(require('./router'));
  32 +
  33 +module.exports = app;
  1 +/**
  2 + * 品牌一览 api
  3 + * @author: ghw<hongwei.gao@yoho.cn>
  4 + * @date: 2016/9/29
  5 + */
  6 +'use strict';
  7 +const api = global.yoho.API;
  8 +const serviceAPI = global.yoho.ServiceAPI;
  9 +
  10 +/**
  11 + * 分开取数,品牌一览 顶部的轮翻广告及热门品牌数据-PC
  12 + * 顶部的轮翻广告及热门品牌数据
  13 + * @param string $contentCode 获取广告资源需要的位置码
  14 + */
  15 +const getBrandTopData = (contentCode) => {
  16 + return serviceAPI.get('operations/api/v5/resource/get', {
  17 + content_code: contentCode
  18 + }, {cache: 3600});
  19 +};
  20 +
  21 +/**
  22 + * 分开取数,获取品牌一览 "按字母'A-Z'分组的品牌列表数据"
  23 + * @param int $channel 频道标识 1:男,2:女,3:潮童,4:创意生活
  24 + */
  25 +const getBrandListData = channel => {
  26 + let params = {method: 'app.brand.brandlist'};
  27 +
  28 + if (!isNaN(channel)) {
  29 + params.yh_channel = channel;
  30 + }
  31 + return api.get('', params);
  32 +};
  33 +
  34 +/**
  35 + * 获取品牌简介
  36 + *
  37 + * @param integer $brandId 品牌ID
  38 + * @param int 用户ID
  39 + * @return array 品牌介绍信息
  40 + */
  41 +const getBrandIntro = (brandId, uid) => {
  42 + let param = {};
  43 +
  44 + if (!uid) {
  45 + param.cache = 3600;
  46 + }
  47 + return api.get('', {
  48 + method: 'app.brand.getBrandIntro',
  49 + brand_id: brandId,
  50 + uid: uid
  51 + }, param);
  52 +};
  53 +
  54 +/**
  55 + * 获取品牌中产品图片
  56 + * @param int 品牌ID
  57 + * @return array 品牌产品信息
  58 + */
  59 +const getProductByBrand = (brandId, limit) => {
  60 + return api.get('', {
  61 + method: 'web.search.search',
  62 + brand: brandId,
  63 + limit: limit
  64 + });
  65 +};
  66 +
  67 +/**
  68 + * 获取品牌信息
  69 + *
  70 + * @param array $ids
  71 + * @return array
  72 + */
  73 +const getBrandInfoByIds = (ids) => {
  74 + return api.get('', {
  75 + method: 'web.brand.info',
  76 + ids: ids instanceof Array ? ids.join(',') : parseInt(ids, 10)
  77 + }, {cache: 3600});
  78 +};
  79 +
  80 +/**
  81 + * 获取品牌列表
  82 + *
  83 + * @param int $brandType
  84 + * @param string $gender
  85 + * @param string $type
  86 + * @return array
  87 + */
  88 +const getPlusstarList = (brandType, gender) => {
  89 + return serviceAPI.get('guang/api/v3/plustar/getlist', {
  90 + gender: gender,
  91 + brand_type: brandType
  92 + }, {cache: 3600});
  93 +};
  94 +
  95 +module.exports = {
  96 + getBrandTopData,
  97 + getBrandListData,
  98 + getBrandIntro,
  99 + getProductByBrand,
  100 + getPlusstarList,
  101 + getBrandInfoByIds
  102 +};
  1 +
  2 +/**
  3 + * 品牌一览 model
  4 + * @author: ghw<hongwei.gao@yoho.cn>
  5 + * @date: 2016/9/29
  6 + */
  7 +'use strict';
  8 +
  9 +const Promise = require('bluebird');
  10 +const co = Promise.coroutine;
  11 +const _ = require('lodash');
  12 +const helpers = global.yoho.helpers;
  13 +const brandApi = require('./brands-api');
  14 +const serviceApi = global.yoho.ServiceAPI;
  15 +
  16 +// 品牌一览资源位CODE码
  17 +const channelCode = {
  18 + boys_brand: '8b16b7baf9a66fbe553a6caa97d2ce2a',
  19 + girls_brand: 'c95ae9e40f0add10549b819f821ad626',
  20 + kids_brand: 'c575c6bfdfa4125fae7d24bbec7119c8',
  21 + lifestyle_brand: '84b7926282fdef92f1039bdcf77c18ba',
  22 + brand_list: 'f0f72b1e8f30e6ad086dfc4401f3a856', // 品牌列表资源位CODE码
  23 + brand_plusstar_banner_boys: 'd0149783b8dd2adaf083fd10556c39a9',
  24 + brand_plusstar_banner_girls: 'aad7a43e9a04ac7c70ae7f0c1acf86ef',
  25 + brand_plusstarindex_boys: 'a833aed63d28457156310e97faa7fa37', // plusstarindex男首资源位
  26 + brand_plusstarindex_girls: '6e4f162be3b3ba44f3bfcf1c38bdb745' // plusstarindex女首资源位
  27 +};
  28 +
  29 +const BOYS = 'boys';
  30 +const GIRLS = 'girls';
  31 +const KIDS = 'kids';
  32 +const LIFESTYLE = 'lifestyle';
  33 +
  34 +/**
  35 + * 获取品牌一览资源位&channelType
  36 + *
  37 + * @param string $channelStr
  38 + * @return array
  39 + */
  40 +const switchBrandParams = channel => {
  41 + let req = {};
  42 +
  43 + switch (channel) {
  44 +
  45 + case BOYS:
  46 + req = {
  47 + channelType: 1,
  48 + brandCode: channelCode.brand_plusstar_banner_boys
  49 + };
  50 + break;
  51 + case GIRLS:
  52 + req = {
  53 + channelType: 2,
  54 + brandCode: channelCode.brand_plusstar_banner_girls
  55 + };
  56 + break;
  57 + case KIDS:
  58 + req = {
  59 + channelType: 3,
  60 + brandCode: channelCode.kids_brand
  61 + };
  62 + break;
  63 + case LIFESTYLE:
  64 + req = {
  65 + channelType: 4,
  66 + brandCode: channelCode.lifestyle_brand
  67 + };
  68 + break;
  69 + default:
  70 + req = {
  71 + channelType: 1,
  72 + brandCode: channelCode.boys_brand
  73 + };
  74 + break;
  75 + }
  76 + return req;
  77 +};
  78 +
  79 +/**
  80 + * 获取品牌一览页面,品牌top
  81 + * @param string $channel 频道名称
  82 + */
  83 +const getBrandViewTop = channel => {
  84 + return co(function*() {
  85 + let switchParams = switchBrandParams(channel);
  86 +
  87 + let res = yield brandApi.getBrandTopData(switchParams.brandCode);
  88 +
  89 + let result = {},
  90 + brandAds = [],
  91 + brandLogos = [];
  92 +
  93 + if (!res || res.code !== 200) {
  94 + return result;
  95 + }
  96 +
  97 + // 头部10个品牌小图块 url
  98 + if (res.data && res.data instanceof Array && res.data[1].data && res.data[1].data.list) {
  99 +
  100 + _.forEach(res.data[1].data.list, subValue => {
  101 + brandAds.push({
  102 + name: subValue.name,
  103 + src: helpers.image(subValue.src, 80, 50, 3),
  104 + url: subValue.url
  105 + });
  106 + });
  107 + }
  108 +
  109 + // 头部品牌图块,广告位
  110 + if (res.data && res.data instanceof Array && res.data[0].data) {
  111 + _.forEach(res.data[0].data, (subValue, k) => {
  112 + let srcUrl;
  113 +
  114 + // kids lifestyle 第一张图尺寸不同
  115 + if (switchParams.channelType === 1 || switchParams.channelType === 2) {
  116 + srcUrl = helpers.image(subValue.src, 222, 180, 3);
  117 + } else {
  118 + srcUrl = (k === 0) ? helpers.image(subValue.src, 570, 280, 3) :
  119 + helpers.image(subValue.src, 280, 280, 3);
  120 + }
  121 + let brandPlusstarItem = {
  122 + name: subValue.title,
  123 + src: srcUrl,
  124 + url: subValue.url
  125 + };
  126 +
  127 + if (channel === BOYS || channel === GIRLS) {
  128 + if (k === 0) {
  129 + brandPlusstarItem.url = helpers.urlFormat('/brands/plusstar', {channel: channel});
  130 + } else {
  131 + brandPlusstarItem.url = helpers.urlFormat('/brands/plusstar', {id: k, channel: channel});
  132 + }
  133 + }
  134 +
  135 + brandLogos.push(brandPlusstarItem);
  136 + });
  137 + }
  138 +
  139 + // 整合brandTop数据结构,boys、girls
  140 + if (switchParams.channelType === 1 || switchParams.channelType === 2) {
  141 + result.isTab = true;
  142 + }
  143 + result.tabHeader = brandLogos;
  144 + result.logos = brandAds;
  145 +
  146 + return result;
  147 + })();
  148 +};
  149 +
  150 +/**
  151 + * 获取品牌一览list
  152 + * @param string $channel 频道名称
  153 + * @param int start 开始位置 1 开始
  154 + * @param int length 取数长度 0 取到最后
  155 + */
  156 +const getBrandViewList = (channel, start, length) => {
  157 + return co(function*() {
  158 + let switchParams = switchBrandParams(channel);
  159 +
  160 + let res = yield brandApi.getBrandListData(switchParams.channelType);
  161 +
  162 + let result = [],
  163 + navigation = [];
  164 +
  165 + if (!res || res.code !== 200) {
  166 + return result;
  167 + }
  168 +
  169 + // 品牌list A-Z 0-9
  170 + if (res.data && res.data.brands) {
  171 +
  172 + _.forEach(res.data.brands, (subValue, key) => {
  173 + let listTmp = [];
  174 +
  175 + _.forEach(subValue, ssubValue => {
  176 + // 为品牌名称
  177 + let href;
  178 +
  179 + if (switchParams.channelType === 1) {
  180 + href = helpers.urlFormat('', {gender: '1,3'}, ssubValue.brand_domain);
  181 + } else if (switchParams.channelType === 2) {
  182 + href = helpers.urlFormat('', {gender: '2,3'}, ssubValue.brand_domain);
  183 + } else {
  184 + href = helpers.urlFormat('', '', ssubValue.brand_domain);
  185 + }
  186 + let brandItem = {
  187 + name: ssubValue.brand_name,
  188 + key: ssubValue.id,
  189 + href: href
  190 + };
  191 +
  192 + if (ssubValue.is_hot === 'Y') {
  193 + brandItem.hot = 'hot';
  194 + }
  195 + listTmp.push(brandItem);
  196 +
  197 + });
  198 + navigation.push(key);
  199 +
  200 + result.push({
  201 + key: key,
  202 + val: _.sortBy(listTmp, 'name')// 对name排序
  203 + });
  204 + });
  205 +
  206 + }
  207 +
  208 + // 只取部分数据
  209 + let begin;
  210 +
  211 +
  212 + if (start) {
  213 + begin = (start - 1) ? (start - 1) : 0;
  214 + begin = (begin > 0) ? begin : 0;
  215 + result = length ? result.slice(begin, length + begin) : result.slice(begin);
  216 + }
  217 +
  218 + result.navigation = navigation;
  219 +
  220 + return result;
  221 + })();
  222 +};
  223 +
  224 +/**
  225 + * 获取单个广告浮窗内容
  226 + *
  227 + * @param int $brandId
  228 + * @param int $uid
  229 + * @return array
  230 + */
  231 +const getBrandInfo = (brandId, uid) => {
  232 + return co(function*() {
  233 + let data = {},
  234 + imgs = [];
  235 +
  236 + // 获取品牌简介
  237 + let res = yield brandApi.getBrandIntro(brandId, uid);
  238 +
  239 + if (!res || res.code !== 200) {
  240 + return data;
  241 + }
  242 + if (res.data) {
  243 + // 获取品牌下的产品信息
  244 + let proInfo = yield brandApi.getProductByBrand(brandId, 3);
  245 +
  246 + if (!proInfo || proInfo.code !== 200) {
  247 + return data;
  248 + }
  249 + let proInfoTmp = proInfo.data.product_list ? proInfo.data.product_list : [];
  250 +
  251 + if (!_.isEmpty(proInfoTmp)) {
  252 + _.forEach(proInfoTmp, subValue => {
  253 + imgs.push({
  254 + src: helpers.image(subValue.default_images, 80, 100, 3)
  255 + });
  256 + });
  257 + }
  258 +
  259 + // 整合
  260 + data = {
  261 + key: res.data.brand_id,
  262 + icon: helpers.image(res.data.brand_ico, 80, 50, 3),
  263 + title: res.data.brand_name,
  264 + content: res.data.brand_intro,
  265 + subtitle: 'FEATURED ITEMS',
  266 + imgs: imgs
  267 + };
  268 + }
  269 + return data;
  270 + })();
  271 +};
  272 +
  273 +/**
  274 + * 多个品牌ID获取品牌信息
  275 + *
  276 + * @param array $brandIds
  277 + * @return array
  278 + */
  279 +const getBrandInfoByIds = (brandIds) => {
  280 + return co(function*() {
  281 + let res = yield brandApi.getBrandInfoByIds(brandIds);
  282 +
  283 + let brandsInfo = {};
  284 +
  285 + if (!res || res.code !== 200) {
  286 + return brandsInfo;
  287 + }
  288 + if (res.data && res.code === 200) {
  289 + _.forEach(res.data, (subValue, k) => {
  290 + subValue.desc = _.trim(subValue.brand_intro.replace(/(\t)|(\n)|(\r)|(&nbsp;)/g, '')
  291 + .replace(/<.*?>/ig, ''));
  292 + subValue.url = subValue.brand_domain;
  293 + delete subValue.brand_intro;
  294 + brandsInfo[k] = subValue;
  295 + });
  296 + }
  297 +
  298 + return brandsInfo;
  299 + })();
  300 +};
  301 +
  302 +/**
  303 + * 获取plusstar品牌列表项目
  304 + *
  305 + * @param string $channel
  306 + * @return array
  307 + */
  308 +const getPlusstarBrandListItem = (channel) => {
  309 + return co(function*() {
  310 + let code = channel === 'girls' ? channelCode.brand_plusstar_banner_girls :
  311 + channelCode.brand_plusstar_banner_boys;
  312 +
  313 + // 资源位数据
  314 + let resource = yield serviceApi.get('operations/api/v5/resource/get', {content_code: code}, {cache: 3600});
  315 +
  316 + let items = [];
  317 +
  318 + if (!resource || resource.code !== 200) {
  319 + return items;
  320 + }
  321 + if (resource.data && resource.code === 200) {
  322 + items[0] = {name: '所有品牌', src: '', url: helpers.urlFormat('/brands', {channel: channel}), brandType: ''};
  323 + items[1] = {name: '设计新潮', src: '', url: '', brandType: 4};
  324 + items[2] = {name: '潮流经典', src: '', url: '', brandType: 1};
  325 + items[3] = {name: '明星潮牌', src: '', url: '', brandType: 2};
  326 + items[4] = {name: '原创潮牌', src: '', url: '', brandType: 3};
  327 +
  328 + let resourceData = resource.data,
  329 + pos = 0;
  330 +
  331 + _.forEach(items, (subValue, k) => {
  332 + if (_.isEmpty(subValue.url)) {
  333 + subValue.url = helpers.urlFormat('/brands/plusstar', {id: k, channel: channel});
  334 + }
  335 +
  336 + if (pos in resourceData[0].data) {
  337 + subValue.src = helpers.image(resourceData[0].data[pos].src, 222, 180, 1);
  338 + subValue.name = resourceData[0].data[pos].title;
  339 + }
  340 + pos++;
  341 + });
  342 + }
  343 +
  344 + return items;
  345 + })();
  346 +};
  347 +
  348 +/**
  349 + * 获取Plustar列表
  350 + *
  351 + * @param string $brandType
  352 + * @param string $gender
  353 + * @return array
  354 + */
  355 +const getPlustarList = (brandType, gender) => {
  356 + return co(function*() {
  357 + let list = yield brandApi.getPlusstarList(brandType, gender);
  358 +
  359 + let brandList = {},
  360 + data = {},
  361 + brandsIds = [],
  362 + result = {brandsIds: [], data: {}};
  363 +
  364 + if (!list || list.code !== 200) {
  365 + return result;
  366 + }
  367 + if (list.data && list.data.data && list.data.data.list) {
  368 + brandList = list.data.data.list[0];
  369 + }
  370 + if (brandList.data) {
  371 + _.forEach(brandList.data, brand => {
  372 + let src = '';
  373 +
  374 + if (brand.data[0]) {
  375 + src = helpers.image(brand.data[0].src, 320, 160, 1);
  376 + }
  377 + data[brand.brand_id] = {
  378 + brand_id: brand.brand_id,
  379 + name: brand.brand_name,
  380 + sort_id: brand.sort_id,
  381 + src: src,
  382 + desc: '',
  383 + url: ''
  384 + };
  385 + brandsIds.push(brand.brand_id);
  386 + });
  387 +
  388 + result.brandsIds = brandsIds;
  389 + result.data = data;
  390 + }
  391 +
  392 + return result;
  393 + })();
  394 +};
  395 +
  396 +module.exports = {
  397 + getBrandViewTop,
  398 + getBrandViewList,
  399 + getBrandInfo,
  400 + getBrandInfoByIds,
  401 + getPlusstarBrandListItem,
  402 + getPlustarList
  403 +};
  1 +/**
  2 + * 品牌一览 controller
  3 + * @author: ghw<hongwei.gao@yoho.cn>
  4 + * @date: 2016/9/29
  5 + */
  6 +'use strict';
  7 +
  8 +const Promise = require('bluebird');
  9 +const co = Promise.coroutine;
  10 +const api = global.yoho.API;
  11 +const headerModel = require('../../../doraemon/models/header');
  12 +const brandsModel = require('./brands-model');
  13 +const _ = require('lodash');
  14 +const helpers = global.yoho.helpers;
  15 +const pager = require(`${global.utils}/pager`).setPager;
  16 +
  17 +const BOYS = 'boys';
  18 +const GIRLS = 'girls';
  19 +const KIDS = 'kids';
  20 +const LIFESTYLE = 'lifestyle';
  21 +
  22 +/**
  23 + * 获取品牌一览资源位&channelType
  24 + *
  25 + * @param string $channelStr
  26 + * @return array
  27 + */
  28 +const getGenderByChannel = channel => {
  29 + let gender = '';
  30 +
  31 + switch (channel) {
  32 +
  33 + case BOYS:
  34 + gender = '1,3';
  35 + break;
  36 + case GIRLS:
  37 + gender = '2,3';
  38 + break;
  39 + default:
  40 + gender = '1,2,3';
  41 + break;
  42 + }
  43 + return gender;
  44 +};
  45 +
  46 +const getHomeurlByChannel = channel => {
  47 + let home;
  48 +
  49 + switch (channel) {
  50 + case GIRLS:
  51 + home = helpers.urlFormat('/woman', {}, 'new');
  52 + break;
  53 + case LIFESTYLE:
  54 + home = helpers.urlFormat('/lifestyle', {}, 'new');
  55 + break;
  56 + case KIDS:
  57 + home = helpers.urlFormat('/kids', {}, 'new');
  58 + break;
  59 + default:
  60 + home = helpers.urlFormat('');
  61 + break;
  62 + }
  63 + return home;
  64 +};
  65 +
  66 +// 添加网站的SEO
  67 +const seoMap = {
  68 + boys: {
  69 + title: '品牌一览|男装品牌排行榜,男装品牌大全|YOHO!BUY 有货 100%正品保证',
  70 + keywords: '品牌一览,男装品牌,男装品牌排行榜,男装品牌大全,YOHO!BUY 有货',
  71 + description: 'YOHO!BUY 有货男装品牌一览汇集国内国际各大男装品牌大全,为广大爱好时尚的男士青年提供品牌男装、' +
  72 + '休闲男装、商务男装.YOHO!BUY 有货,100%正品保证'
  73 + },
  74 + girls: {
  75 + title: '品牌一览|女装品牌排行榜,女装品牌大全|YOHO!BUY 有货 100%正品保证',
  76 + keywords: '品牌一览,女装品牌,女装品牌排行榜,女装品牌大全,YOHO!BUY 有货',
  77 + description: 'YOHO!BUY 有货女装品牌一览汇集国内国际各大女装品牌,为广大爱美女生提供品牌女装、休闲女装、' +
  78 + '商务女装.买品牌女装就上YOHO!BUY 有货,100%正品保证'
  79 + },
  80 + kids: {
  81 + title: '品牌一览|童装童鞋品牌,儿童鞋包配饰排行榜,潮童品牌大全|YOHO!BUY 有货 100%正品保证',
  82 + keywords: '童装品牌,童装童鞋排行榜,儿童鞋包配饰排行榜,潮童品牌大全,品牌一览,YOHO!BUY 有货',
  83 + description: 'YOHO!BUY 有货童装品牌一览汇集国内国际各大童装品牌大全,为广大爱好潮流的儿童提供品牌童装、童鞋,' +
  84 + '儿童鞋包配饰.YOHO!BUY 有货,100%正品保证'
  85 + },
  86 + lifestyle: {
  87 + title: '品牌一览|数码3c,居家,玩具娱乐,文具,美妆品牌|YOHO!BUY 有货 100%正品保证',
  88 + keywords: '数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,美妆品牌',
  89 + description: 'YOHO!BUY 有货女装品牌一览汇集国内国际各大数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,' +
  90 + '美妆品牌.买创意生活家居就上YOHO!BUY 有货,100%正品保证'
  91 + }
  92 +};
  93 +
  94 +/**
  95 + * 获取品牌一览list
  96 + * @param string $channel 频道名称
  97 + * @param int start 开始位置 1 开始
  98 + * @param int length 取数长度 0 取到最后
  99 + */
  100 +exports.getBrandViewList = (channel) => {
  101 + let apiMethod = [
  102 + headerModel.requestHeaderData(channel),
  103 + brandsModel.getBrandViewTop(channel),
  104 + brandsModel.getBrandViewList(channel, 1, 5) // 分屏加载
  105 + ];
  106 +
  107 + return api.all(apiMethod).then(result => {
  108 + let responseData = {
  109 + module: 'brands',
  110 + page: 'brands'
  111 + };
  112 +
  113 + // 头部数据
  114 + Object.assign(responseData, result[0]);
  115 +
  116 + // 品牌一览列表
  117 + responseData.brands = result[1];
  118 + responseData.brands.navigation = result[2].navigation;
  119 + responseData.brands.category = result[2];
  120 +
  121 + // 导航pathNav
  122 + responseData.brands.pathNav = [
  123 + {
  124 + href: getHomeurlByChannel(channel),
  125 + name: `${_.toUpper(channel)}首页`,
  126 + pathTitle: 'YOHO!BUY 有货'
  127 + },
  128 + {
  129 + href: helpers.urlFormat('/brands'),
  130 + name: '品牌一览',
  131 + pathTitle: '品牌一览'
  132 + }
  133 + ];
  134 +
  135 + // SEO
  136 + Object.assign(responseData, seoMap[channel]);
  137 +
  138 + return responseData;
  139 + });
  140 +};
  141 +
  142 +/**
  143 + * brandList-Ajax调用
  144 + */
  145 +exports.getBrandList = (channel, start) => {
  146 + let apiMethod = [
  147 + brandsModel.getBrandViewList(channel, start)
  148 + ];
  149 +
  150 + return api.all(apiMethod).then(result => {
  151 + let responseData = {};
  152 +
  153 + // 品牌一览列表
  154 + responseData.category = result[0];
  155 + return responseData;
  156 + });
  157 +};
  158 +
  159 +/**
  160 + * 品牌接口数据
  161 + *
  162 + * @param string brandId 获取品牌ID
  163 + * @return json
  164 + */
  165 +exports.brandInfo = (brandId, uid) => {
  166 + let apiMethod = [
  167 + brandsModel.getBrandInfo(brandId, uid)
  168 + ];
  169 +
  170 + return api.all(apiMethod).then(result => {
  171 +
  172 + return {
  173 + code: _.isEmpty(result[0]) ? 400 : 200,
  174 + brand: _.isEmpty(result[0]) ? '' : result[0]
  175 + };
  176 + });
  177 +};
  178 +
  179 +/**
  180 + * 多个品牌ID获取品牌信息
  181 + *
  182 + * @param array $brandIds
  183 + * @return array
  184 + */
  185 +exports.plusstarList = (channel, req) => {
  186 + return co(function*() {
  187 + let headerData = yield headerModel.requestHeaderData(channel);
  188 +
  189 + let responseData = {
  190 + module: 'brands',
  191 + page: 'brands',
  192 + brandsHomePage: true
  193 + };
  194 +
  195 + let id = req.query.id || '',
  196 + gender = req.query.gender || getGenderByChannel(channel),
  197 + limit = 20,
  198 + page = parseInt(req.query.page, 10) || 1;
  199 +
  200 + let items = yield brandsModel.getPlusstarBrandListItem(channel);
  201 +
  202 + let brandType = 1;
  203 +
  204 + if (items[id]) {
  205 + brandType = items[id].brandType;
  206 + }
  207 + let plustarList = yield brandsModel.getPlustarList(brandType, gender);
  208 +
  209 + let list = plustarList.data;
  210 +
  211 + let brandIds = [],
  212 + brands = [],
  213 + pageList = {};
  214 +
  215 + brandIds = plustarList.brandsIds.slice((page - 1) * limit, page * limit);
  216 + if (plustarList.brandsIds.length > limit) {
  217 + pageList = pager(Math.ceil(plustarList.brandsIds.length / limit), {
  218 + channel: channel,
  219 + page: page,
  220 + id: id
  221 + });
  222 + }
  223 +
  224 + if (brandIds.length > 0) {
  225 + // 获取品牌信息
  226 + let brandsInfo = yield brandsModel.getBrandInfoByIds(brandIds);
  227 +
  228 + _.forEach(brandIds, brandId => {
  229 + if (brandsInfo[brandId]) {
  230 + list[brandId].desc = brandsInfo[brandId].desc;
  231 + list[brandId].url = helpers.urlFormat('', {gender: gender}, brandsInfo[brandId].url);
  232 + }
  233 + brands.push(list[brandId]);
  234 + });
  235 + }
  236 + let data = {
  237 + brandsHomePage: true,
  238 + brands: {
  239 + items: brands,
  240 + tabs: items
  241 +
  242 + }
  243 + };
  244 +
  245 + // 头部数据
  246 + Object.assign(responseData, headerData);
  247 +
  248 + // 产品信息
  249 + Object.assign(responseData, data);
  250 +
  251 + // 页码
  252 + Object.assign(responseData, pageList);
  253 +
  254 + // SEO
  255 + Object.assign(responseData, seoMap[channel]);
  256 + return responseData;
  257 + })();
  258 +
  259 +};
  1 +/**
  2 + * router of sub app brands
  3 + * @author: ghw<hongwei.gao@yoho.cn>
  4 + * @date: 2016/09/29
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const express = require('express');
  10 +const cRoot = './controllers';
  11 +
  12 +const router = express.Router(); // eslint-disable-line
  13 +const brandsController = require(`${cRoot}/brands`);
  14 +
  15 +// 品牌一览
  16 +router.get('', brandsController.index);
  17 +
  18 +// 悬浮出现品牌信息
  19 +router.get('/brandinfo', brandsController.brandInfo);
  20 +
  21 +// 品牌没有加载完全,继续加载
  22 +router.post('/brandList', brandsController.brandList);
  23 +
  24 +// brands/plusstar
  25 +router.get('/plusstar', brandsController.plusstarList);
  26 +
  27 +module.exports = router;
  1 +<div class="home-page yoho-page brands" data-page="brands">
  2 + {{# brands}}
  3 +
  4 + {{> common/path-nav}}
  5 +
  6 + {{#if isTab}}
  7 + <div class="brands-tabs">
  8 + <ul class="clearfix">
  9 + {{#each tabHeader}}
  10 + <li>
  11 + <a href="{{url}}">
  12 + <div class="g-mask"></div>
  13 + <p class="tips">{{name}}</p>
  14 + <img class="lazy" data-original="{{src}}"/>
  15 + </a>
  16 + </li>
  17 + {{/each}}
  18 + </ul>
  19 + <div class="hover-contain">
  20 + <div class="hoverarr">
  21 + <i></i>
  22 + </div>
  23 + </div>
  24 + </div>
  25 + {{^}}
  26 + <ul class="brands-ad clearfix">
  27 + {{#each tabHeader}}
  28 + <li>
  29 + <a href="{{url}}" target="_blank">
  30 + <img class="lazy" data-original="{{src}}">
  31 + </a>
  32 + </li>
  33 + {{/each}}
  34 + </ul>
  35 + {{/if}}
  36 + <div class="brands-logo clearfix">
  37 + {{#each logos}}
  38 + <a href="{{url}}" title="{{name}}" target="_blank">
  39 + <img class="lazy" data-original="{{src}}">
  40 + </a>
  41 + {{/each}}
  42 + </div>
  43 + <div class="brands-category">
  44 + <div class="category-nav">
  45 + <span>BRANDS A-Z:</span>
  46 + {{#each navigation}}
  47 + <a href="#{{this}}">{{this}}</a>
  48 + {{/each}}
  49 + </div>
  50 + </div>
  51 + <div class="brands-list" >
  52 + {{> brand-list}}
  53 + </div>
  54 + {{/ brands}}
  55 +</div>
  1 +{{> layout/header}}
  2 +<div class="home-page yoho-page brands" data-page="brands">
  3 +{{# brands}}
  4 +
  5 +
  6 + {{! 头部banner}}
  7 + {{# slide}}
  8 + {{>index/slide-banner}}
  9 + {{/ slide}}
  10 +
  11 + {{! 品牌 BRAND}}
  12 + {{# brand}}
  13 + {{> index/floor-header}}
  14 + <div class="brandfloor clearfix">
  15 + <ul class="g-list">
  16 + {{# list}}
  17 + <li>
  18 + <a href="{{url}}" target= "_blank">
  19 + <img class="lazy" data-original="{{src}}" alt="">
  20 +
  21 + </a>
  22 + </li>
  23 + {{/ list}}
  24 + </ul>
  25 + </div>
  26 + {{/ brand}}
  27 +
  28 + {{! 单品 SINGLE GOODS}}
  29 + {{# singlegoods}}
  30 + {{> index/floor-header}}
  31 + <div class="singlegoods clearfix">
  32 + <ul class="g-list">
  33 + {{# list}}
  34 + <li>
  35 + <a href="{{url}}" target= "_blank">
  36 + <img class="lazy" data-original="{{src}}" alt="">
  37 + <div class="singlegoods-title">
  38 + <div class="g-mask"></div>
  39 + <p>{{name}}</p>
  40 + </div>
  41 + </a>
  42 + </li>
  43 + {{/ list}}
  44 + </ul>
  45 + </div>
  46 + {{/ singlegoods}}
  47 +
  48 + {{!视频 VIDEO}}
  49 + {{# video}}
  50 + {{> index/floor-header}}
  51 + <div class="video clearfix">
  52 + <ul class="g-list">
  53 + {{# list}}
  54 + <li>
  55 + <a href="{{url}}" target= "_blank">
  56 + <img class="lazy" data-original="{{src}}" alt="" /><i class="video-play"></i>
  57 + <div class="video-title">
  58 + <div class="g-mask"></div>
  59 + <p>{{name}}</p>
  60 + </div>
  61 + </a>
  62 + </li>
  63 + {{/ list}}
  64 + </ul>
  65 + </div>
  66 + {{/ video}}
  67 +
  68 + {{!新闻 NEWS}}
  69 + {{# news}}
  70 + {{> index/floor-header}}
  71 + <div class="news clearfix">
  72 + <div class="news-pic">
  73 + {{# pics}}
  74 + {{>index/slide-banner}}
  75 + {{/ pics}}
  76 + </div>
  77 + <div class="news-txt">
  78 + {{# txts}}
  79 + <ul>
  80 + {{#each list}}
  81 + <li>
  82 + <i class="iconfont">&#xe619;</i><a href="{{url}}">{{name}}</a>
  83 + </li>
  84 + {{/each}}
  85 + </ul>
  86 + {{/ txts}}
  87 + </div>
  88 + </div>
  89 + {{/ news}}
  90 +
  91 + {{!推广 AD}}
  92 + {{# ads}}
  93 + <div class="ads clearfix">
  94 + <ul class="g-list">
  95 + {{# list}}
  96 + <li>
  97 + <a href="{{url}}" target= "_blank">
  98 + <img class="lazy" data-original="{{src}}" alt="">
  99 + <span class="name g-title">{{name}}</span>
  100 + <span class="des g-title">{{des}}</span>
  101 + </a>
  102 + </li>
  103 + {{/ list}}
  104 + </ul>
  105 + </div>
  106 + {{/ ads}}
  107 +{{/ brands}}
  108 +</div>
  109 +{{> layout/footer}}
  1 +{{> layout/header}}
  2 +<div class="home-page yoho-page brands" data-page="brands">
  3 +{{# brands}}
  4 + <div class="sit-nav">
  5 + <a href="#">BOYS首页</a><span class="sep">></span><a href="#">品牌一览</a>
  6 + </div>
  7 + <div class="brands-tabs height-initial">
  8 + <ul class="clearfix">
  9 + {{#each tabs}}
  10 + <li>
  11 + <a href="{{url}}" target="_blank">
  12 + <div class="g-mask"></div>
  13 + <p class="tips">{{name}}</p>
  14 + <img class="lazy" data-original="{{src}}"/>
  15 + </a>
  16 + </li>
  17 + {{/each}}
  18 + </ul>
  19 + <div class="hover-contain">
  20 + <div class="hoverarr">
  21 + <i></i>
  22 + </div>
  23 + </div>
  24 + </div>
  25 + <div class="brands-items clearfix">
  26 + {{#each items}}
  27 + <div class="brands-item clearfix">
  28 + <a class="brands-pic" title="{{name}}" href="{{url}}" target="_blank">
  29 + <img class="lazy" data-original="{{src}}"/>
  30 + </a>
  31 + <div class="brand-info">
  32 + <a title="{{name}}" href="{{url}}" target="_blank">
  33 + <h3>
  34 + {{name}}
  35 + </h3>
  36 + </a>
  37 + <div class="brand-desc">{{desc}}</div>
  38 + </div>
  39 + </div>
  40 + {{/each}}
  41 + </div>
  42 + <div class="pagination">
  43 + <a href="#" class="page_pre" title="上一页"><i class="iconfont">&#xe60f;</i>上一页</a>
  44 + <a href="#"><span>1</span></a>
  45 + <a href="#" class="cur"><span>2</span></a>
  46 + <a href="#"><span>3</span></a>
  47 + <a href="#"><span>4</span></a>
  48 + <a href="#"><span>5</span></a>
  49 + <a><span>...</span></a>
  50 + <a href="#"><span>215</span></a>
  51 + <a href="#" title="下一页">下一页<i class="iconfont">&#xe60e;</i></a>
  52 + </div>
  53 +{{/ brands}}
  54 +</div>
  55 +{{> layout/footer}}
  1 +<div class="home-page yoho-page brands" data-page="brands">
  2 +{{# brands}}
  3 +
  4 + {{> common/path-nav}}
  5 +
  6 + <div class="brands-tabs height-initial">
  7 + <ul class="clearfix">
  8 + {{#each tabs}}
  9 + <li>
  10 + <a href="{{url}}">
  11 + <div class="g-mask"></div>
  12 + <p class="tips">{{name}}</p>
  13 + <img class="lazy" data-original="{{src}}"/>
  14 + </a>
  15 + </li>
  16 + {{/each}}
  17 + </ul>
  18 + <div class="hover-contain">
  19 + <div class="hoverarr">
  20 + <i></i>
  21 + </div>
  22 + </div>
  23 + </div>
  24 + <div class="brands-items clearfix">
  25 + {{#each items}}
  26 + <div class="brands-item clearfix">
  27 + <a class="brands-pic" title="{{name}}" href="{{url}}" target="_blank">
  28 + <img class="lazy" data-original="{{src}}"/>
  29 + </a>
  30 + <div class="brand-info">
  31 + <a title="{{name}}" href="{{url}}" target="_blank">
  32 + <h3>
  33 + {{name}}
  34 + </h3>
  35 + </a>
  36 + <div class="brand-desc">{{{desc}}}</div>
  37 + </div>
  38 + </div>
  39 + {{/each}}
  40 + </div>
  41 +{{/ brands}}
  42 + <div class="pagination clearfix">
  43 + {{# prePage}}
  44 + <a href="{{url}}" title="上一页">上一页<span class="iconfont">&#xe60e;</span></a>
  45 + {{/ prePage}}
  46 +
  47 + {{# pages}}
  48 + <a{{#if url}} href="{{url}}"{{/if}}{{#if cur}} class="cur"{{/if}}>{{num}}</a>
  49 + {{/ pages}}
  50 +
  51 + {{# nextPage}}
  52 + <a href="{{url}}" title="下一页">下一页<span class="iconfont">&#xe60c;</span></a>
  53 + {{/ nextPage}}
  54 + </div>
  55 +</div>
  1 +{{> layout/header}}
  2 +<div class="home-page yoho-page brands" data-page="brands">
  3 +{{# brands}}
  4 +
  5 +
  6 + {{! 头部banner}}
  7 + {{# slide}}
  8 + {{>index/slide-banner}}
  9 + {{/ slide}}
  10 +
  11 + {{! 品牌 BRAND}}
  12 + {{# brand}}
  13 + {{> index/floor-header}}
  14 + <div class="brandfloor list-floor clearfix">
  15 + <ul class="g-list">
  16 + {{# list}}
  17 + <li>
  18 + <a href="{{href}}" target= "_blank">
  19 + <img class="lazy" data-original="{{img}}" alt="">
  20 +
  21 + </a>
  22 + </li>
  23 + {{/ list}}
  24 + </ul>
  25 + </div>
  26 + {{/ brand}}
  27 +
  28 + {{! 单品 SINGLE GOODS}}
  29 + {{# singlegoods}}
  30 + {{> index/floor-header}}
  31 + <div class="singlegoods list-floor clearfix">
  32 + <ul class="g-list">
  33 + {{# list}}
  34 + <li>
  35 + <a href="{{href}}" target= "_blank">
  36 + <img class="lazy" data-original="{{img}}" alt="">
  37 + <div class="singlegoods-title">
  38 + <div class="g-mask"></div>
  39 + <p>{{name}}</p>
  40 + </div>
  41 + </a>
  42 + </li>
  43 + {{/ list}}
  44 + </ul>
  45 + </div>
  46 + {{/ singlegoods}}
  47 +
  48 + {{!视频 VIDEO}}
  49 + {{# video}}
  50 + {{> index/floor-header}}
  51 + <div class="video list-floor clearfix">
  52 + <ul class="g-list">
  53 + {{# list}}
  54 + <li>
  55 + <a href="{{href}}" target= "_blank">
  56 + <img class="lazy" data-original="{{img}}" alt="" /><i class="video-play"></i>
  57 + <div class="video-title">
  58 + <div class="g-mask"></div>
  59 + <p>{{name}}</p>
  60 + </div>
  61 + </a>
  62 + </li>
  63 + {{/ list}}
  64 + </ul>
  65 + </div>
  66 + {{/ video}}
  67 +
  68 + {{!新闻 NEWS}}
  69 + {{# news}}
  70 + {{> index/floor-header}}
  71 + <div class="news clearfix">
  72 + <div class="news-pic">
  73 + {{# pics}}
  74 + {{>index/slide-banner}}
  75 + {{/ pics}}
  76 + </div>
  77 + <div class="news-txt">
  78 + {{# txts}}
  79 + <ul>
  80 + {{#each list}}
  81 + <li>
  82 + <i class="iconfont">&#xe619;</i><a href="{{href}}" target= "_blank">{{name}}</a>
  83 + </li>
  84 + {{/each}}
  85 + </ul>
  86 + {{/ txts}}
  87 + </div>
  88 + </div>
  89 + {{/ news}}
  90 +
  91 + {{!推广 AD}}
  92 + {{# ads}}
  93 + <div class="ads list-floor clearfix">
  94 + <ul class="g-list">
  95 + {{# list}}
  96 + <li>
  97 + <a href="{{href}}" target= "_blank">
  98 + <img class="lazy" data-original="{{img}}" alt="">
  99 + <span class="name g-title">{{name}}</span>
  100 + <span class="des g-title">{{des}}</span>
  101 + </a>
  102 + </li>
  103 + {{/ list}}
  104 + </ul>
  105 + </div>
  106 + {{/ ads}}
  107 +{{/ brands}}
  108 +</div>
  109 +{{> layout/footer}}
  1 +{{#each category}}
  2 + <dl class="clearfix" name="{{key}}">
  3 + <dt>{{key}}</dt>
  4 + <dd>
  5 + <ul class="clearfix">
  6 + {{#each val}}
  7 + <li>
  8 + <a class="{{hot}}" data-key="{{key}}" href="{{href}}" target="_blank">
  9 + <span>{{name}}</span>
  10 + </a>
  11 + {{# hot}}
  12 + <i class="iconfont">&#xe62c;</i>
  13 + {{/ hot}}
  14 + </li>
  15 + {{/each}}
  16 + </ul>
  17 + </dd>
  18 + </dl>
  19 +{{/each}}
  1 +/**
  2 + * Created by TaoHuang on 2016/10/19.
  3 + */
  4 +
  5 +'use strict';
  6 +
  7 +const service = require('../models/cart-service');
  8 +
  9 +const getProductInfo = (req, res, next) => {
  10 + let pid = req.query.productId || '';
  11 +
  12 + service.getProductInfoAsync(pid).then((result) => {
  13 + return res.render('goods-detail', Object.assign({
  14 + layout: false
  15 + }, result));
  16 + }).catch(next);
  17 +};
  18 +
  19 +
  20 +module.exports = {
  21 + getProductInfo
  22 +};
  1 +/**
  2 + * sub app cart
  3 + * @author: htoooth<ht.anglenx@gmail.com>
  4 + * @date: 2016/10/19
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +var express = require('express'),
  10 + path = require('path'),
  11 + hbs = require('express-handlebars');
  12 +
  13 +var app = express();
  14 +
  15 +// set view engin
  16 +var doraemon = path.join(__dirname, '../../doraemon/views'); //parent view root
  17 +
  18 +app.on('mount', function(parent) {
  19 + delete parent.locals.settings; // 不继承父 App 的设置
  20 + Object.assign(app.locals, parent.locals);
  21 +});
  22 +
  23 +app.set('views', path.join(__dirname, 'views/action'));
  24 +app.engine('.hbs', hbs({
  25 + extname: '.hbs',
  26 + defaultLayout: 'layout',
  27 + layoutsDir: doraemon,
  28 + partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
  29 + helpers: global.yoho.helpers
  30 +}));
  31 +
  32 +// router
  33 +app.use(require('./router'));
  34 +
  35 +module.exports = app;
  1 +/**
  2 + * 商品详情models
  3 + * @author: xuqi<qi.xu@yoho.cn>
  4 + * @date: 2016/5/6
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const Promise = require('bluebird');
  10 +const co = Promise.coroutine;
  11 +const _ = require('lodash');
  12 +const helpers = global.yoho.helpers;
  13 +
  14 +const productAPI = require('./product-api');
  15 +
  16 +const _getProductIntroAsync = (productSkn) => {
  17 + return co(function * () {
  18 + let result = yield Promise.props({
  19 + sizeInfo: productAPI.sizeInfoAsync(productSkn)
  20 + });
  21 +
  22 + return result;
  23 + })();
  24 +};
  25 +
  26 +/**
  27 + * 获得sku商品数据
  28 + */
  29 +const _getSkuDataByProductBaseInfo = (data) => {
  30 + let totalStorageNum = 0;
  31 + let skuGoods = null;// sku商品
  32 + let defaultImage = '';// 默认图
  33 + let chooseSkuFlag = false; // 选中状态
  34 +
  35 + if (_.isEmpty(_.get(data, 'goods_list', []))) {
  36 + return {
  37 + totalStorageNum,
  38 + skuGoods,
  39 + defaultImage
  40 + };
  41 + }
  42 +
  43 + skuGoods = _.get(data, 'goods_list', []).reduce((acc, cur, pos)=> {
  44 +
  45 + // 如果status为0,即skc下架时就跳过该商品$value['status'] === 0
  46 + let goodsGroup = {};
  47 +
  48 + if (_.isEmpty(cur.color_image)) {
  49 + return acc;
  50 + }
  51 +
  52 + if (cur.images_list) {
  53 + // 商品列表
  54 + goodsGroup.productSkc = cur.product_skc;
  55 + goodsGroup.src = helpers.image(cur.color_image, 40, 40);
  56 + goodsGroup.title = `${_.trim(data.product_name)} ${cur.color_name}`;
  57 + goodsGroup.name = cur.color_name;
  58 + goodsGroup.focus = false;
  59 + goodsGroup.total = 0;
  60 + goodsGroup.thumbs = [];
  61 + goodsGroup.size = [];
  62 + }
  63 +
  64 + _.get(cur, 'images_list', []).forEach((good) => {
  65 + if (good.image_url) {
  66 + goodsGroup.thumbs.push({
  67 + url: '',
  68 + shower: helpers.image(good.image_url, 420, 560),
  69 + img: helpers.image(good.image_url, 75, 100)
  70 + });
  71 + }
  72 + });
  73 +
  74 + // 缩略图空,不显示
  75 + if (_.isEmpty(goodsGroup.thumbs)) {
  76 + return acc;
  77 + }
  78 +
  79 + // 默认第一张图片
  80 + if (pos === 0) {
  81 + defaultImage = helpers.image(cur.color_image, 420, 560);
  82 + }
  83 +
  84 + // 商品的尺码列表
  85 + _.get(cur, 'size_list', []).forEach((size) => {
  86 + if (data.attribute === 3) {
  87 + // 虚拟商品,门票默认最大为4,
  88 + size.storage_number = size.storage_number > 4 ? 4 : size.storage_number;
  89 + }
  90 +
  91 + // 如果status为0,即skc下架时就跳过该商品
  92 + if (cur.status === 0) {
  93 + size.storage_number = 0;
  94 + }
  95 +
  96 + goodsGroup.size.push({
  97 + name: size.size_name,
  98 + sku: size.product_sku,
  99 + num: _.parseInt(size.storage_number),
  100 + goodsId: size.size_id
  101 + });
  102 +
  103 + // 单个sku商品的总数
  104 + goodsGroup.total += _.parseInt(size.storage_number);
  105 +
  106 + if (goodsGroup.total > 0 && !chooseSkuFlag) { // 默认选中该sku商品
  107 + goodsGroup.focus = true;
  108 + chooseSkuFlag = true;// 选中sku商品
  109 + }
  110 +
  111 + totalStorageNum += _.parseInt(size.storage_number);
  112 +
  113 + });
  114 +
  115 + acc.push(goodsGroup);
  116 + return acc;
  117 + }, []);
  118 +
  119 + if (!_.isEmpty(skuGoods) && !chooseSkuFlag) { // 没有选中一个sku商品,默认选中第一个sku商品
  120 + _.head(skuGoods).focus = true;
  121 + }
  122 +
  123 + return {
  124 + defaultImage: defaultImage,
  125 + skuGoods: skuGoods,
  126 + totalStorageNum: totalStorageNum
  127 + };
  128 +};
  129 +
  130 +/**
  131 + * 使sizeBoList id以 sizeAttributeBos id顺序一样
  132 + * @param sizeInfoBo
  133 + */
  134 +const _sizeInfoBoSort = (sizeInfoBo) => {
  135 + if (!sizeInfoBo.sizeBoList || !sizeInfoBo.sizeAttributeBos) {
  136 + return {};
  137 + }
  138 +
  139 + _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> {
  140 + let sortAttr = {};
  141 +
  142 + sizeBoList.sortAttributes.forEach(sortAttributes => {
  143 + sortAttr[sortAttributes.id] = sortAttributes;
  144 + });
  145 +
  146 + sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
  147 + });
  148 +
  149 + _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> {
  150 + let sortAttr = [];
  151 +
  152 + sizeInfoBo.sizeAttributeBos.forEach(val => {
  153 + if (sizeBoList.sortAttributes[val.id]) {
  154 + sortAttr.push(sizeBoList.sortAttributes[val.id]);
  155 + }
  156 + });
  157 +
  158 + sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
  159 +
  160 + });
  161 +
  162 + return sizeInfoBo;
  163 +};
  164 +
  165 +/**
  166 + * 获取尺寸信息
  167 + * @param sizeInfo
  168 + * @returns {{}}
  169 + */
  170 +const _getSizeData = (sizeInfo) => {
  171 +
  172 + // 尺码信息
  173 + if (!_.has(sizeInfo, 'sizeInfoBo')) {
  174 + return {};
  175 + }
  176 +
  177 + sizeInfo.sizeInfoBo = _sizeInfoBoSort(sizeInfo.sizeInfoBo);
  178 +
  179 + let boyReference = _.get(sizeInfo, 'productExtra.boyReference', false);
  180 + let girlReference = _.get(sizeInfo, 'productExtra.girlReference', false);
  181 + let gender = _.get(sizeInfo, 'productDescBo.gender', 3);
  182 + let referenceName = (function() {
  183 + if (gender === 3 && boyReference) {
  184 + return '参考尺码(男)';
  185 + } else if (gender === 3 && girlReference) {
  186 + return '参考尺码(女)';
  187 + } else {
  188 + return '参考尺码';
  189 + }
  190 + }());
  191 +
  192 + // 判断是否显示参考尺码
  193 + let showReference = (boyReference && _.get(sizeInfo, 'sizeInfoBo.sizeBoList[0].boyReferSize', false)) ||
  194 + (girlReference && _.get(sizeInfo, 'sizeInfoBo.sizeBoList[0].girlReferSize', false));
  195 +
  196 + if (!_.has(sizeInfo, 'sizeInfoBo.sizeAttributeBos')) {
  197 + return {};
  198 + }
  199 +
  200 + // 尺码信息头部
  201 + let size = {
  202 + thead: [{name: '吊牌尺码', id: ''}],
  203 + tbody: []
  204 + };
  205 +
  206 + // 显示参考尺码
  207 + if (showReference) {
  208 + size.thead[1] = {name: referenceName, id: ''};
  209 + }
  210 +
  211 + _.get(sizeInfo, 'sizeInfoBo.sizeAttributeBos', []).forEach((value) => {
  212 + size.thead.push({
  213 + name: value.attributeName || ' ',
  214 + id: value.id
  215 + });
  216 + });
  217 +
  218 + _.get(sizeInfo, 'sizeInfoBo.sizeBoList', []).forEach((value) => {
  219 + let sizes = [];
  220 +
  221 + // 吊牌尺码
  222 + sizes.push(value.sizeName);
  223 +
  224 + // 判断是否显示参考尺码
  225 + if (boyReference && (gender === 1 || gender === 3) && showReference) {
  226 + sizes.push(_.get(value, 'boyReferSize.referenceName', ' '));
  227 + } else if (girlReference && (gender === 2 || gender === 3) && showReference) {
  228 + sizes.push(_.get(value, 'girlReferSize.referenceName', ' '));
  229 + } else {
  230 + if (size.thead[1] && showReference) {
  231 + size.thead[1] = {};
  232 + }
  233 + }
  234 +
  235 + // 其他尺码信息
  236 + _.get(value, 'sortAttributes', []).forEach(attr => {
  237 + sizes.push(_.get(attr, 'sizeValue', ' '));
  238 + });
  239 +
  240 + // 尺码信息
  241 + size.tbody.push(sizes);
  242 + });
  243 +
  244 + // 参考尺码为空
  245 + if (_.isEmpty(size.thead[1]) && showReference) {
  246 + // 移除这个值
  247 + size.thead.splice(1, 1);
  248 + }
  249 +
  250 + // 测量方式
  251 + if (sizeInfo.sizeImage) {
  252 + size.sizeImg = sizeInfo.sizeImage;
  253 + }
  254 +
  255 + return size;
  256 +};
  257 +
  258 +/**
  259 + * 商品尺码信息
  260 + *
  261 + * @param productSkn
  262 + * @param maxSortId
  263 + * @return object
  264 + */
  265 +const _getIntroInfo = (productSkn, additionalData)=> {
  266 + if (!productSkn) {
  267 + return {};
  268 + }
  269 +
  270 + let sizeInfo = additionalData.sizeInfo;
  271 +
  272 + if (_.isEmpty(sizeInfo)) {
  273 + return {};
  274 + }
  275 +
  276 + let result = {};
  277 +
  278 + // 尺寸数据
  279 + result.size = _getSizeData(sizeInfo);
  280 +
  281 + return result;
  282 +};
  283 +
  284 +/**
  285 + * 详情页数据格式化
  286 + * @param origin Object 原始数据
  287 + * @return result Object 格式化数据
  288 + */
  289 +const _detailDataPkg = (origin) => {
  290 + return co(function*() {
  291 + if (_.isEmpty(origin) || _.isEmpty(origin)) {
  292 + return {};
  293 + }
  294 +
  295 + let result = {};
  296 +
  297 + let propOrigin = _.partial(_.get, origin);
  298 +
  299 + // 商品名称
  300 + if (!propOrigin('product_name')) {
  301 + return result;
  302 + }
  303 +
  304 + result.name = propOrigin('product_name');
  305 + result.skn = propOrigin('product_skn');
  306 + result.productId = propOrigin('product_id');
  307 +
  308 + // 商品价格
  309 + result.marketPrice = propOrigin('format_market_price');
  310 + result.salePrice = propOrigin('format_sales_price');
  311 + result.hasOtherPrice = true;
  312 +
  313 + if (result.salePrice === '0') {
  314 + delete result.salePrice;
  315 + result.hasOtherPrice = false;
  316 + }
  317 +
  318 + // 上市期
  319 + if (propOrigin('expect_arrival_time')) {
  320 + result.arrivalDate = `${propOrigin('expect_arrival_time')}月`;
  321 + result.presalePrice = propOrigin('format_sales_price');
  322 + delete result.salePrice;
  323 + result.hasOtherPrice = false;
  324 + }
  325 +
  326 + // sku商品信息
  327 + let skuData = _getSkuDataByProductBaseInfo(origin);
  328 +
  329 + // 商品购买状态
  330 + let soldOut = !!(propOrigin('status') === 0 || skuData.totalStorageNum === 0);
  331 + let notForSale = propOrigin('attribute') === 2; // 非卖品
  332 + let virtualGoods = propOrigin('attribute') === 3; // 虚拟商品
  333 +
  334 + if (!soldOut && !notForSale && !virtualGoods) {
  335 + result.addToCart = 1;
  336 + }
  337 +
  338 + result.colors = skuData.skuGoods;
  339 +
  340 + return result;
  341 + })();
  342 +};
  343 +
  344 +/**
  345 + * 获取某一个商品详情主页面
  346 + */
  347 +const getProductInfoAsync = (pid) => {
  348 + return co(function * () {
  349 + if (!pid) {
  350 + return {};
  351 + }
  352 +
  353 + // 获取商品基本信息
  354 + let productData = yield productAPI.getProductAsync(pid);
  355 +
  356 + if (_.isEmpty(productData.data)) {
  357 + return Promise.reject({
  358 + code: 404,
  359 + message: 'app.product.data api wrong'
  360 + });
  361 + }
  362 +
  363 + let productSkn = _.get(productData, 'data.product_skn');
  364 +
  365 + let requestData = yield Promise.all([
  366 + _getProductIntroAsync(productSkn), // 商品详细介绍
  367 + _detailDataPkg(productData.data) // 商品详细价格
  368 + ]);
  369 +
  370 + let productDescription = requestData[0];
  371 + let productInfo = requestData[1];
  372 +
  373 + let intro = _getIntroInfo(productSkn, productDescription);
  374 +
  375 + return Object.assign(productInfo, intro);
  376 + })();
  377 +};
  378 +
  379 +module.exports = {
  380 + getProductInfoAsync // 获取某一个商品详情主页面
  381 +};
  1 +/**
  2 + * Created by TaoHuang on 2016/10/19.
  3 + */
  4 +
  5 +const api = global.yoho.API;
  6 +
  7 +const sizeInfoAsync = skn => {
  8 + return api.get('', {
  9 + method: 'h5.product.intro',
  10 + productskn: skn
  11 + });
  12 +
  13 +};
  14 +
  15 +/**
  16 + * 获得产品信息
  17 + * @param pid
  18 + * @returns {Promise.<type>}
  19 + */
  20 +const getProductAsync = (pid) => {
  21 +
  22 + return api.get('', {
  23 + method: 'app.product.data',
  24 + product_id: pid
  25 + });
  26 +};
  27 +
  28 +module.exports = {
  29 + sizeInfoAsync,
  30 + getProductAsync
  31 +};
  1 +/**
  2 + * router of sub app cart
  3 + * @author: htoooth<ht.anglenx@gmail.com>
  4 + * @date: 2016/10/19
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const router = require('express').Router(); // eslint-disable-line
  10 +const cRoot = './controllers';
  11 +
  12 +const cart = require(`${cRoot}/cart`);
  13 +
  14 +router.get('/index/getProductInfo', cart.getProductInfo);
  15 +
  16 +// Your controller here
  17 +
  18 +module.exports = router;
  1 +<div class="detail-header">
  2 + <span class="colse">X关闭</span>
  3 +</div>
  4 +<div class="detail-body">
  5 + <span class="magnify"></span>
  6 + {{#colors}}
  7 + <div class="detail-bigpic {{#unless focus}}none{{/unless}}">
  8 + {{#thumbs}}
  9 + <div class="bigpic">
  10 + <img src="{{shower}}">
  11 + </div>
  12 + {{/thumbs}}
  13 + <div class="piclist">
  14 + <span class="pre"></span>
  15 + <div class="con">
  16 + <ul>
  17 + {{#thumbs}}
  18 + <li><img src="{{img}}"></li>
  19 + {{/thumbs}}
  20 + </ul>
  21 + </div>
  22 + <span class="next"></span>
  23 + </div>
  24 + </div>
  25 + {{/colors}}
  26 + <div class="detail-info">
  27 + <div class="title">
  28 + <h2>{{name}}</h2>
  29 + </div>
  30 + <div class="type">
  31 + <span class="type-s">新品</span>
  32 + </div>
  33 + <div class="price">
  34 +
  35 + {{#if salePrice}}
  36 + <span class="oldprice">原价:<del>¥{{marketPrice}}</del></span>
  37 + <span class="newprice">现价:<b class="promotion-price">¥{{salePrice}}</b></span>
  38 + {{^}}
  39 + <span class="newprice {{#presalePrice}}none{{/presalePrice}}">原价:<b class="promotion-price">¥{{marketPrice}}</b></span>
  40 + {{/if}}
  41 +
  42 + {{#if presalePrice}}
  43 + <span class="oldprice">原价:<del>¥{{marketPrice}}</del></span>
  44 + <span class="newprice">预售价:<b class="promotion-price">¥{{presalePrice}}</b></span>
  45 + {{/if}}
  46 + {{#arrivalDate}}
  47 + <span class="arrivalDate">上市期:{{arrivalDate}}</span>
  48 + {{/arrivalDate}}
  49 + </div>
  50 + <div class="order">
  51 + <dl>
  52 + <dd class="colorBox">选颜色:</dd>
  53 + <dt>
  54 + <div class="colorBox">
  55 + <ul>
  56 + {{#colors}}
  57 + <li class="color">
  58 + <p class="{{#if focus}}atcive{{/if}}"><span></span><img src="{{src}}"></p>
  59 + <span>{{name}}</span>
  60 + </li>
  61 + {{/colors}}
  62 + </ul>
  63 + </div>
  64 + </dt>
  65 + <dd class="">选尺码:</dd>
  66 + <dt>
  67 + {{#colors}}
  68 + <div class="showSizeBox {{#unless focus}}none{{/unless}}">
  69 + {{#size}}
  70 + <span data-sku="{{sku}}" data-num="{{num}}">{{name}}</span>
  71 + {{/size}}
  72 + </div>
  73 + {{/colors}}
  74 + </dt>
  75 + <dd>选件数:</dd>
  76 + <dt>
  77 + <div class="amount_wrapper">
  78 + <i class="amount cut"></i>
  79 + <input type="text" id="mnum" class="mnum" value="1" readonly="readonly">
  80 + <i class="amount add"></i>
  81 +
  82 + </div>
  83 + </dt>
  84 + </dl>
  85 + </div>
  86 + <div class="submit">
  87 + <input class="addcart" type="button">
  88 + <input class="btn_pre_sale none" type="button">
  89 + <input class="btn_sellout none" type="button">
  90 + <input class="fav_count" type="button">
  91 + </div>
  92 + </div>
  93 +
  94 + <div class="detail-size">
  95 + <h3>尺码信息<span>(单位:厘米)</span></h3>
  96 + {{# size}}
  97 + <table>
  98 + <thead>
  99 + <tr>
  100 + {{# thead}}
  101 + <td width="{{width}}">{{name}}</td>
  102 + {{/ thead}}
  103 + </tr>
  104 + </thead>
  105 + <tbody>
  106 + {{# tbody}}
  107 + <tr>
  108 + {{#each .}}
  109 + <td>{{.}}</td>
  110 + {{/each}}
  111 + </tr>
  112 + {{/ tbody}}
  113 + </tbody>
  114 + </table>
  115 + {{/ size}}
  116 + <div class="size-info">
  117 + ※ 以上尺寸为实物实际测量,因测量方式不同会有略微误差,相关数据仅作参考,以收到实物为准。
  118 + </div>
  119 + </div>
  120 +</div>
  121 +<input value="{{addToCart}}" id="addToCart" type="hidden" />
  1 +/**
  2 + * 最近浏览controller
  3 + * @author: xuqi<qi.xu@yoho.cn>
  4 + * @date: 2016/10/11
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const rvModel = require('../models/recent-view');
  10 +const _ = require('lodash');
  11 +
  12 +const index = (req, res, next) => {
  13 +
  14 + let limit = req.query.limit;
  15 +
  16 + let browserSkn = decodeURIComponent(req.cookies._browseskn);
  17 +
  18 + // 拆解skn
  19 + let skn = browserSkn ? browserSkn.replace(/\-(\d)+(\,){0,1}/g, ',') : '';
  20 +
  21 + // 去除skn字符串的最后一个多余的,
  22 + if (skn && skn.lastIndexOf(',') === skn.length - 1) {
  23 + skn = skn.slice(0, -1);
  24 + }
  25 +
  26 + if (!skn) {
  27 + res.jsonp({
  28 + code: 200,
  29 + data: [],
  30 + message: 'User info'
  31 + });
  32 + } else {
  33 + skn = _.slice(_.uniq(skn.split(',')), 0, limit).join(','); // 去重+截取
  34 + rvModel.index(skn, limit).then(data => {
  35 + res.jsonp(data);
  36 + }).catch(next);
  37 + }
  38 +
  39 +};
  40 +
  41 +module.exports = {
  42 + index
  43 +};
  1 +/**
  2 + * sub app common
  3 + * @author: xuqi<qi.xu@yoho.cn>
  4 + * @date: 2016/10/11
  5 + */
  6 +
  7 +var express = require('express'),
  8 + path = require('path'),
  9 + hbs = require('express-handlebars');
  10 +
  11 +var app = express();
  12 +
  13 +// set view engin
  14 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
  15 +
  16 +app.on('mount', function(parent) {
  17 + delete parent.locals.settings; // 不继承父 App 的设置
  18 + Object.assign(app.locals, parent.locals);
  19 +});
  20 +
  21 +app.set('views', path.join(__dirname, 'views/action'));
  22 +app.engine('.hbs', hbs({
  23 + extname: '.hbs',
  24 + defaultLayout: 'layout',
  25 + layoutsDir: doraemon,
  26 + partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
  27 + helpers: global.yoho.helpers
  28 +}));
  29 +
  30 +// router
  31 +app.use(require('./router'));
  32 +
  33 +module.exports = app;
  1 +/**
  2 + * recent view model
  3 + * @author: xuqi<qi.xu@yoho.cn>
  4 + * @date: 2016/10/11
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const _ = require('lodash');
  10 +const api = global.yoho.API;
  11 +const helper = global.yoho.helpers;
  12 +
  13 +const index = (skn, limit) => {
  14 +
  15 + return api.get('', {
  16 + method: 'h5.product.batch',
  17 + productSkn: skn,
  18 + limit: limit
  19 + }).then(result => {
  20 +
  21 + if (result.code === 200) {
  22 + let data = [];
  23 + let historyProduct = result.data.product_list;
  24 +
  25 + _.forEach(historyProduct, hp => {
  26 + if (!hp) {
  27 + return;
  28 + }
  29 +
  30 + let mp = hp.market_price;
  31 + let sp = hp.sales_price;
  32 +
  33 + let defaultGoods = _.find(hp.goods_list, {is_default: 'Y'});
  34 +
  35 + // 无默认商品取商品列表第一个
  36 + if (!defaultGoods) {
  37 + defaultGoods = hp.goods_list[0];
  38 + }
  39 +
  40 + data.push({
  41 + market_price: mp === sp ? '' : ${helper.round(mp, 2)}`,
  42 + price: ${helper.round(sp, 2)}`,
  43 + product_name: hp.product_name,
  44 + url: helper.urlFormat(
  45 + `/product/pro_${hp.product_id}_${defaultGoods.goods_id}/${hp.cn_alphabet}.html`, '', 'item'),
  46 + pic_url: helper.image(defaultGoods.images_url, 150, 200, 2, 70)
  47 + });
  48 + });
  49 +
  50 + return {
  51 + code: 200,
  52 + data: data,
  53 + message: result.message
  54 + };
  55 + } else {
  56 +
  57 + // get list error
  58 + return {
  59 + code: result.code,
  60 + message: result.message,
  61 + data: []
  62 + };
  63 + }
  64 + });
  65 +};
  66 +
  67 +module.exports = {
  68 + index
  69 +};
  1 +/**
  2 + * router of sub app common
  3 + * @author: xuqi<qi.xu@yoho.cn>
  4 + * @date: 2016/10/11
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const router = require('express').Router(); // eslint-disable-line
  10 +const cRoot = './controllers';
  11 +
  12 +const rvCtrl = require(`${cRoot}/recent-view`);
  13 +
  14 +router.get('/recentReview', rvCtrl.index); // 最近浏览
  15 +
  16 +module.exports = router;
@@ -15,6 +15,14 @@ const _ = require('lodash'); @@ -15,6 +15,14 @@ const _ = require('lodash');
15 * 找回密码主页面 15 * 找回密码主页面
16 */ 16 */
17 const index = (req, res, next) => { 17 const index = (req, res, next) => {
  18 + // 清除cookie
  19 + res.clearCookie('_UID', {
  20 + domain: 'yohobuy.com'
  21 + });
  22 + res.clearCookie('_TOKEN', {
  23 + domain: 'yohobuy.com'
  24 + });
  25 +
18 service.indexPageDataAsync() 26 service.indexPageDataAsync()
19 .then(result => { 27 .then(result => {
20 res.render('back/index', Object.assign({ 28 res.render('back/index', Object.assign({
@@ -33,38 +33,33 @@ function doPassportCallback(req, res, user) { @@ -33,38 +33,33 @@ function doPassportCallback(req, res, user) {
33 refer = config.siteUrl; 33 refer = config.siteUrl;
34 } 34 }
35 if (user.openId) { 35 if (user.openId) {
36 - let signinByOpenID;  
37 -  
38 user.nickname = _.trim(user.nickname); 36 user.nickname = _.trim(user.nickname);
39 37
40 if (user.sourceType === 'wechat') { 38 if (user.sourceType === 'wechat') {
41 -  
42 - // PC 的微信登录之前使用了 open_id, 所以需要特别的接口处理  
43 - signinByOpenID = AuthHelper.signinByWechat(  
44 - user.nickname, user.openId, user.unionId, user.sourceType, shoppingKey);  
45 - } else {  
46 - signinByOpenID = AuthHelper.signinByOpenID(  
47 - user.nickname, user.openId, user.sourceType, shoppingKey); 39 + // PC 的微信登录使用unionId
  40 + user.openId = user.unionId;
48 } 41 }
49 42
50 - return signinByOpenID.then((result) => {  
51 - if (result.code !== 200) {  
52 - return Promise.reject(result);  
53 - }  
54 - if (result.data['is_bind'] && result.data['is_bind'] === 'N') { //eslint-disable-line  
55 - return helpers.urlFormat('/passport/thirdlogin/index', {  
56 - openId: user.unionId || user.openId,  
57 - sourceType: user.sourceType,  
58 - refer: refer  
59 - });  
60 - } else if (result.code === 200 && result.data.uid) {  
61 - return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {  
62 - return refer;  
63 - });  
64 - }  
65 - }).then((redirectTo) => {  
66 - return res.redirect(redirectTo);  
67 - }); 43 + return AuthHelper.signinByOpenID(user.nickname, user.openId, user.sourceType, shoppingKey)
  44 + .then((result) => {
  45 + if (result.code !== 200) {
  46 + return res.redirect(config.siteUrl);
  47 + }
  48 + if (result.data['is_bind'] && result.data['is_bind'] === 'N') { //eslint-disable-line
  49 + return helpers.urlFormat('/passport/thirdlogin/index', {
  50 + openId: user.unionId || user.openId,
  51 + sourceType: user.sourceType,
  52 + refer: refer
  53 + });
  54 + } else if (result.code === 200 && result.data.uid) {
  55 + return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
  56 + return refer;
  57 + });
  58 + }
  59 + })
  60 + .then((redirectTo) => {
  61 + return res.redirect(redirectTo);
  62 + });
68 } else { 63 } else {
69 return Promise.resolve(res.redirect(loginPage)); 64 return Promise.resolve(res.redirect(loginPage));
70 } 65 }
@@ -75,7 +70,7 @@ const common = { @@ -75,7 +70,7 @@ const common = {
75 let refer = req.query.refer || req.get('Referer'); 70 let refer = req.query.refer || req.get('Referer');
76 71
77 refer && res.cookie('refer', encodeURI(refer), { 72 refer && res.cookie('refer', encodeURI(refer), {
78 - domain: 'yohobuy.com' 73 + domain: '.yohobuy.com'
79 }); 74 });
80 next(); 75 next();
81 }, 76 },
@@ -122,11 +117,7 @@ const local = { @@ -122,11 +117,7 @@ const local = {
122 areaName = area ? area.name : ''; 117 areaName = area ? area.name : '';
123 } 118 }
124 119
125 - let refer = req.query.refer;  
126 -  
127 - refer && res.cookie('refer', encodeURI(refer), {  
128 - domain: 'yohobuy.com'  
129 - }); 120 + let refer = req.query.refer || req.cookies.refer || req.get('Referer');
130 121
131 PassportHelper.getLeftBannerAsync(SIGNIN_LEFT_BANNER_CODE).then(cover => { 122 PassportHelper.getLeftBannerAsync(SIGNIN_LEFT_BANNER_CODE).then(cover => {
132 res.render('login', { 123 res.render('login', {
@@ -176,13 +167,13 @@ const local = { @@ -176,13 +167,13 @@ const local = {
176 let refer = (function() { 167 let refer = (function() {
177 if (/sign|login|reg|passport/.test(_.get(req, 'cookies.refer', ''))) { 168 if (/sign|login|reg|passport/.test(_.get(req, 'cookies.refer', ''))) {
178 return `${config.siteUrl}/home`; 169 return `${config.siteUrl}/home`;
179 - } else if (_.has(req, 'cookies.refer')) { 170 + } else if (_.get(req, 'cookies.refer')) {
180 return decodeURI(req.cookies.refer); 171 return decodeURI(req.cookies.refer);
181 } else { 172 } else {
182 return `${config.siteUrl}/home`; 173 return `${config.siteUrl}/home`;
183 } 174 }
184 }()); 175 }());
185 - 176 + console.log(user.uid);
186 AuthHelper.syncUserSession(user.uid, req, res).then(() => { 177 AuthHelper.syncUserSession(user.uid, req, res).then(() => {
187 res.json({ 178 res.json({
188 code: 200, 179 code: 200,
@@ -6,6 +6,11 @@ const aes = require('./aes-pwd'); @@ -6,6 +6,11 @@ const aes = require('./aes-pwd');
6 const cache = global.yoho.cache; 6 const cache = global.yoho.cache;
7 const sign = global.yoho.sign; 7 const sign = global.yoho.sign;
8 const api = global.yoho.API; 8 const api = global.yoho.API;
  9 +const cookie = global.yoho.cookie;
  10 +
  11 +const Promise = require('bluebird');
  12 +
  13 +const cartService = require('./cart-service');
9 14
10 const Auth = { 15 const Auth = {
11 signin(type, area, profile, password, shoppingKey) { 16 signin(type, area, profile, password, shoppingKey) {
@@ -137,12 +142,11 @@ const Auth = { @@ -137,12 +142,11 @@ const Auth = {
137 return api.get('', param); 142 return api.get('', param);
138 }, 143 },
139 syncUserSession(uid, req, res) { 144 syncUserSession(uid, req, res) {
140 - return Auth.profile(uid).then((userInfo) => { 145 + return Promise.all([Auth.profile(uid), cartService.goodsCount(uid)]).spread((userInfo, count) => {
141 let token = sign.makeToken(uid); 146 let token = sign.makeToken(uid);
142 let data = userInfo.data; 147 let data = userInfo.data;
143 let encryptionUid = aes.encryptionUid(data.uid); 148 let encryptionUid = aes.encryptionUid(data.uid);
144 149
145 -  
146 if (data) { 150 if (data) {
147 let uidCookie = `${data.profile_name}::${encryptionUid}::${data.vip_info.title}::${token}`; 151 let uidCookie = `${data.profile_name}::${encryptionUid}::${data.vip_info.title}::${token}`;
148 let isStudent = data.vip_info.is_student || 0; 152 let isStudent = data.vip_info.is_student || 0;
@@ -156,6 +160,16 @@ const Auth = { @@ -156,6 +160,16 @@ const Auth = {
156 res.cookie('isStudent', isStudent, { 160 res.cookie('isStudent', isStudent, {
157 domain: 'yohobuy.com' 161 domain: 'yohobuy.com'
158 }); 162 });
  163 +
  164 + // 购物车中商品的数量
  165 + res.cookie('_g', JSON.stringify({
  166 + _k: cookie.getShoppingKey(req),
  167 + _nac: count,
  168 + _ac: 0,
  169 + _c: 1
  170 + }), {
  171 + domain: 'yohobuy.com'
  172 + });
159 } 173 }
160 req.session._TOKEN = token; // esline-disable-line 174 req.session._TOKEN = token; // esline-disable-line
161 req.session._LOGIN_UID = uid; // esline-disable-line 175 req.session._LOGIN_UID = uid; // esline-disable-line
  1 +/**
  2 + * Created by TaoHuang on 2016/9/28.
  3 + */
  4 +
  5 +'use strict';
  6 +
  7 +const api = global.yoho.API;
  8 +
  9 +const goodsCount = (uid, shoppingKey) => {
  10 + let params = {
  11 + method: 'app.Shopping.count'
  12 + };
  13 +
  14 + if (uid) {
  15 + params.uid = uid;
  16 + }
  17 +
  18 + if (shoppingKey) {
  19 + params.shopping_key = shoppingKey;
  20 + }
  21 +
  22 + return api.get('', params);
  23 +};
  24 +
  25 +module.exports = {
  26 + goodsCount
  27 +};
  1 +/**
  2 + * Created by TaoHuang on 2016/9/28.
  3 + */
  4 +
  5 +'use strict';
  6 +
  7 +
  8 +const api = require('./cart-api');
  9 +
  10 +const _ = require('lodash');
  11 +
  12 +const goodsCount = (uid, shoppingKey) => {
  13 + return api.goodsCount(uid, shoppingKey).then(result => _.get(result, 'data.cart_goods_count', 0));
  14 +};
  15 +
  16 +module.exports = {
  17 + goodsCount
  18 +};
  19 +
@@ -11,189 +11,230 @@ const mRoot = '../models'; @@ -11,189 +11,230 @@ const mRoot = '../models';
11 const service = require(`${mRoot}/detail-service`); 11 const service = require(`${mRoot}/detail-service`);
12 const detailHelper = require(`${mRoot}/detail-helper`); 12 const detailHelper = require(`${mRoot}/detail-helper`);
13 13
14 -const Actions = require('./lib/actions');  
15 -const YohoAction = require('./lib/yoho-action');  
16 const moment = require('moment'); 14 const moment = require('moment');
17 const DEFAULT_AVATAR_ICO = 'http://static.yohobuy.com/images/v3/boy.jpg'; 15 const DEFAULT_AVATAR_ICO = 'http://static.yohobuy.com/images/v3/boy.jpg';
  16 +const SEO_SLOGAN = 'YOHO!BUY 有货';
18 const _ = require('lodash'); 17 const _ = require('lodash');
19 18
20 -class DetailAction extends YohoAction {  
21 - /**  
22 - * 渲染商品详情  
23 - */  
24 - render() {  
25 - const req = this.request;  
26 -  
27 - let pid = req.params[0];  
28 - let gid = req.params[1];  
29 - let saveCurrentGoodsInCookies = _.partial(service.saveRecentGoodInCookies,  
30 - req.cookies._browse,  
31 - req.cookies._browseskn,  
32 - this.response  
33 - );  
34 - let vipLevel = detailHelper.vipLevel(req.user.vip);  
35 - let uid = req.user.uid || 0;  
36 - let isStudent = req.user.isStudent || 0;  
37 -  
38 - return service.showMainAsync({  
39 - pid: pid,  
40 - gid: gid,  
41 - channel: this.getSessionChannel(),  
42 - gender: this.guessUserGender(),  
43 - uid: uid,  
44 - isStudent: isStudent,  
45 - vipLevel: vipLevel,  
46 - saveInCookies: saveCurrentGoodsInCookies  
47 - }).then(result=> {  
48 - const seo = result.seo;  
49 -  
50 - this.setTitle(seo.title, true, '|');  
51 - this.setKeywords(seo.keywords);  
52 - this.setDescription(seo.description, true);  
53 -  
54 - this.setEntry('product', 'detail');  
55 - this.renderTemplate('product/detail', result);  
56 - }).catch(err => {  
57 - if (err.code === 404) {  
58 - return this.next();  
59 - } 19 +const CHANNEL = {
  20 + boys: 'boys',
  21 + girls: 'girls',
  22 + kids: 'kids'
  23 +};
60 24
61 - return this.next(err); 25 +const _getChannel = (req, res) => {
  26 + let channel = req.cookies._Channel;
  27 +
  28 + if (!channel) {
  29 + res.cookie('_Channel', CHANNEL.boys, {
  30 + domain: '.yohobuy.com',
  31 + maxAge: moment.duration(300, 'days').seconds()
62 }); 32 });
  33 +
  34 + channel = 'boys'; // 设置默认值
  35 + }
  36 +
  37 + return channel;
  38 +};
  39 +
  40 +const _getGender = (channel) => {
  41 + switch (channel) {
  42 + case 'boys':
  43 + return '1,3';
  44 + case 'girls':
  45 + return '2,3';
  46 + default:
  47 + return '1,2,3';
  48 + }
  49 +};
  50 +
  51 +/**
  52 + * 首屏渲染商品详情
  53 + */
  54 +const showMain = (req, res, next) => {
  55 + let pid = req.params[0];
  56 + let channel = _getChannel(req, res);
  57 + let gender = _getGender(channel);
  58 +
  59 + const nullUserInfo = {
  60 + uid: null,
  61 + isStudent: null,
  62 + vipLevel: null,
  63 + saveInCookies: null
  64 + };
  65 +
  66 + return service.showMainAsync(Object.assign({
  67 + pid: pid,
  68 + channel: channel,
  69 + gender: gender
  70 + }, nullUserInfo)).then((result)=> {
  71 + return res.render('product/detail', Object.assign({
  72 + module: 'product',
  73 + page: 'detail',
  74 + title_more: true,
  75 + title: _.get(result, 'seo.title', '') + ' | ' + SEO_SLOGAN,
  76 + keywords: _.get(result, 'seo.keywords', '').replace(/~+/, ''),
  77 + description_more: true,
  78 + description: result.description
  79 + }, result));
  80 + }).catch(next);
  81 +
  82 +};
  83 +
  84 +/**
  85 + * 商品价格相关的数据
  86 + */
  87 +const detailHeader = (req, res, next) => {
  88 + let pid = req.query.productId || 0;
  89 + let uid = req.user.uid || 0;
  90 + let vipLevel = detailHelper.vipLevel(req.user.vip);
  91 + let dataMd5 = req.query.md5 || 0;
  92 +
  93 + let saveCurrentGoodsInCookies = _.partial(service.saveRecentGoodInCookies,
  94 + req.cookies._browseskn,
  95 + res
  96 + );
  97 +
  98 + return service.getDetailHeader(pid, uid, req.user.isStudent, vipLevel, dataMd5, saveCurrentGoodsInCookies)
  99 + .then((result) => {
  100 + if (result.code === 200) {
  101 + return res.render('product/detail-header', Object.assign({layout: false}, result.data));
  102 + } else {
  103 + return res.status(204).end();
  104 + }
  105 + }).catch(next);
  106 +};
  107 +
  108 +/**
  109 + * 特殊商品退换货
  110 + */
  111 +const detailReturn = (req, res, next) => {
  112 + let skn = req.query.skn || 0;
  113 +
  114 + if (!skn) {
  115 + return {
  116 + code: 400,
  117 + message: '商品数据出错'
  118 + };
63 } 119 }
64 -} 120 +
  121 + return service.saleReturn(skn).then(result => {
  122 + return res.json({
  123 + code: 200,
  124 + data: {
  125 + result: result
  126 + }
  127 + });
  128 + }).catch(next);
  129 +};
65 130
66 /** 131 /**
67 * 获取热区图 132 * 获取热区图
68 */ 133 */
69 -class HotAreaAction extends YohoAction {  
70 - render() {  
71 - let pid = this.request.query.productId || 0;  
72 -  
73 - return service.indexHotAreaAsync(pid).then(result => {  
74 - this.renderTemplate('product/hotarea', {  
75 - hotArea: result,  
76 - layout: false  
77 - }); 134 +const indexHotArea = (req, res, next) => {
  135 + let pid = req.query.productId || 0;
  136 +
  137 + return service.indexHotAreaAsync(pid).then((result) => {
  138 + res.render('product/hotarea', {
  139 + hotArea: result,
  140 + layout: false
78 }); 141 });
79 - }  
80 -} 142 + }).catch(next);
  143 +};
81 144
82 /** 145 /**
83 * 获得评论列表 146 * 获得评论列表
84 */ 147 */
85 -class CommentAction extends YohoAction {  
86 - render() {  
87 - const req = this.request;  
88 - let pid = req.query.productId || 0;  
89 - let page = req.query.page || 1;  
90 - let size = req.query.size || 10;  
91 -  
92 - return service.getShareOrderListAsync(pid, page, size).then((result) => {  
93 - let pageResponse = _.get(result, 'data.pageResponse', {});  
94 -  
95 - this.response.json({  
96 - code: result.code,  
97 - data: _.get(pageResponse, 'list', []).map((item)=> {  
98 -  
99 - return {  
100 - avatar: _.get(item, 'userInfo.headIco', '') ?  
101 - helpers.image(item.userInfo.headIco, 30, 30) :  
102 - DEFAULT_AVATAR_ICO,  
103 - userName: _.get(item, 'userInfo.nickName', ''),  
104 - date: moment(item.createTime, 'X').format('YYYY-MM-DD HH:mm:ss'),  
105 - color: _.get(item, 'goods.color_name', ''),  
106 - size: _.get(item, 'goods.size_name', ''),  
107 - comment: item.content,  
108 - total: pageResponse.totalCount  
109 - };  
110 - })  
111 - }); 148 +
  149 +const indexComment = (req, res, next) => {
  150 + let pid = req.query.productId || 0;
  151 + let page = req.query.page || 1;
  152 + let size = req.query.size || 10;
  153 +
  154 + return service.getShareOrderListAsync(pid, page, size).then((result) => {
  155 + let pageResponse = _.get(result, 'data.pageResponse', {});
  156 +
  157 + return res.json({
  158 + code: result.code,
  159 + data: _.get(pageResponse, 'list', []).map((item)=> {
  160 +
  161 + return {
  162 + avatar: _.get(item, 'userInfo.headIco', '') ?
  163 + helpers.image(item.userInfo.headIco, 30, 30) :
  164 + DEFAULT_AVATAR_ICO,
  165 + userName: _.get(item, 'userInfo.nickName', ''),
  166 + date: moment(item.createTime, 'X').format('YYYY-MM-DD HH:mm:ss'),
  167 + color: _.get(item, 'goods.color_name', ''),
  168 + size: _.get(item, 'goods.size_name', ''),
  169 + comment: item.content,
  170 + total: pageResponse.totalCount
  171 + };
  172 + })
112 }); 173 });
113 - }  
114 -} 174 + }).catch(next);
  175 +};
115 176
116 /** 177 /**
117 * 获得咨询列表 178 * 获得咨询列表
118 */ 179 */
119 -class ConsultAction extends YohoAction {  
120 - render() {  
121 - const req = this.request;  
122 - let uid = req.user.uid || '';  
123 - let pid = req.query.productId || 0;  
124 - let page = req.query.page || 1;  
125 - let size = req.query.size || 10;  
126 -  
127 - return service.indexConsultAsync(uid, pid, page, size).then(result => {  
128 - this.response.json({  
129 - code: 200,  
130 - data: result  
131 - }); 180 +const indexConsult = (req, res, next) => {
  181 + let uid = req.user.uid || '';
  182 + let pid = req.query.productId || 0;
  183 + let page = req.query.page || 1;
  184 + let size = req.query.size || 10;
  185 +
  186 + return service.indexConsultAsync(uid, pid, page, size).then(result => {
  187 + return res.json({
  188 + code: 200,
  189 + data: result
132 }); 190 });
133 - }  
134 -} 191 + }).catch(next);
  192 +};
135 193
136 /** 194 /**
137 * 新建咨询 195 * 新建咨询
138 */ 196 */
139 -class CreateConsultAction extends YohoAction {  
140 - render() {  
141 - const req = this.request, res = this.response;  
142 - let uid = req.user.uid || '';  
143 - let pid = req.body.productId || 0;  
144 - let content = req.body.content;  
145 -  
146 - if (content && uid) {  
147 - return service.createConsultAsync(uid, pid, content).then(result => {  
148 - res.json(result);  
149 - });  
150 - } else if (!content) {  
151 - res.json({  
152 - code: 400,  
153 - message: '请输入咨询内容'  
154 - });  
155 -  
156 - } else if (!uid) {  
157 - res.json({  
158 - code: 403,  
159 - message: '用户没有登录',  
160 - data: {  
161 - url: helpers.urlFormat('/signin.html')  
162 - }  
163 - });  
164 -  
165 - } else {  
166 - res.json({  
167 - code: 400,  
168 - message: '请输入咨询内容'  
169 - });  
170 - }  
171 - }  
172 -} 197 +const createConsult = (req, res, next) => {
  198 + let uid = req.user.uid || '';
  199 + let pid = req.body.productId || 0;
  200 + let content = req.body.content;
  201 +
  202 + if (content && uid) {
  203 + return service.createConsultAsync(uid, pid, content).then(result => {
  204 + res.json(result);
  205 + }).catch(next);
  206 + } else if (!content) {
  207 + res.json({
  208 + code: 400,
  209 + message: '请输入咨询内容'
  210 + });
173 211
174 -const detailHeader = (req, res, next) => {  
175 - let pid = req.query.productId || 0;  
176 - let uid = req.user.uid || 0;  
177 - let vipLevel = detailHelper.vipLevel(req.user.vip);  
178 - let dataMd5 = req.query.md5 || 0; 212 + } else if (!uid) {
  213 + res.json({
  214 + code: 403,
  215 + message: '用户没有登录',
  216 + data: {
  217 + url: helpers.urlFormat('/signin.html')
  218 + }
  219 + });
179 220
180 - service.getDetailHeader(pid, uid, req.user.isStudent, vipLevel, dataMd5).then((result) => {  
181 - if (result.code === 200) {  
182 - return res.render('product/detail-header', Object.assign({layout: false}, result.data));  
183 - } else {  
184 - return res.status(204).end();  
185 - }  
186 - }).catch(next); 221 + } else {
  222 + res.json({
  223 + code: 400,
  224 + message: '请输入咨询内容'
  225 + });
  226 + }
187 }; 227 };
188 228
189 -module.exports = {  
190 - showMain: Actions.createAction(DetailAction),  
191 - indexHotArea: Actions.createAction(HotAreaAction),  
192 - indexComment: Actions.createAction(CommentAction),  
193 - indexConsult: Actions.createAction(ConsultAction),  
194 - createConsult: Actions.createAction(CreateConsultAction),  
195 - productHeader: detailHeader  
196 229
  230 +module.exports = {
  231 + showMain,
  232 + indexHotArea,
  233 + indexComment,
  234 + indexConsult,
  235 + createConsult,
  236 + productHeader: detailHeader,
  237 + detailReturn
197 }; 238 };
198 239
199 240
@@ -17,10 +17,10 @@ const changeFavoriteBrand = (req, res, next) => { @@ -17,10 +17,10 @@ const changeFavoriteBrand = (req, res, next) => {
17 17
18 if (uid && brandId) { 18 if (uid && brandId) {
19 brandService.changeAsync(uid, brandId).then(result => { 19 brandService.changeAsync(uid, brandId).then(result => {
20 - res.json(result); 20 + return res.json(result);
21 }).catch(next); 21 }).catch(next);
22 } else if (!uid) { 22 } else if (!uid) {
23 - res.json({ 23 + return res.json({
24 code: 403, 24 code: 403,
25 message: '用户ID不存在', 25 message: '用户ID不存在',
26 data: { 26 data: {
@@ -28,13 +28,29 @@ const changeFavoriteBrand = (req, res, next) => { @@ -28,13 +28,29 @@ const changeFavoriteBrand = (req, res, next) => {
28 } 28 }
29 }); 29 });
30 } else { 30 } else {
31 - res.json({ 31 + return res.json({
32 code: 400, 32 code: 400,
33 message: '操作失败' 33 message: '操作失败'
34 }); 34 });
35 } 35 }
36 }; 36 };
37 37
  38 +const isFavoriteBrand = (req, res, next) => {
  39 + let uid = req.user.uid || '';
  40 + let brandId = req.query.brandId;
  41 +
  42 + if (uid && brandId) {
  43 + brandService.isFavoriteAsync(uid, brandId).then(result => {
  44 + return res.json(result);
  45 + }).catch(next);
  46 + } else {
  47 + return res.json({
  48 + code: 400,
  49 + message: '状态失败'
  50 + });
  51 + }
  52 +};
  53 +
38 const collectProduct = (req, res, next) => { 54 const collectProduct = (req, res, next) => {
39 let uid = req.user.uid || ''; 55 let uid = req.user.uid || '';
40 let pid = req.body.productId; 56 let pid = req.body.productId;
@@ -43,8 +59,7 @@ const collectProduct = (req, res, next) => { @@ -43,8 +59,7 @@ const collectProduct = (req, res, next) => {
43 if (uid && pid) { 59 if (uid && pid) {
44 switch (type) { 60 switch (type) {
45 case 'add': 61 case 'add':
46 - {  
47 - productService.createAsync(uid, pid) 62 + productService.createAsync(uid, pid)
48 .then(result => { 63 .then(result => {
49 if (result.code === 413) { 64 if (result.code === 413) {
50 result.message = '该商品已经收藏'; 65 result.message = '该商品已经收藏';
@@ -53,22 +68,17 @@ const collectProduct = (req, res, next) => { @@ -53,22 +68,17 @@ const collectProduct = (req, res, next) => {
53 res.json(result); 68 res.json(result);
54 }) 69 })
55 .catch(next); 70 .catch(next);
56 - break;  
57 - } 71 + break;
58 case 'cancel': 72 case 'cancel':
59 - {  
60 - productService.deleteAsync(uid, pid) 73 + productService.deleteAsync(uid, pid)
61 .then(result => res.json(result)) 74 .then(result => res.json(result))
62 .catch(next); 75 .catch(next);
63 - break;  
64 - } 76 + break;
65 default: 77 default:
66 - {  
67 - res.json({  
68 - code: 400,  
69 - message: '错误类型'  
70 - });  
71 - } 78 + res.json({
  79 + code: 400,
  80 + message: '错误类型'
  81 + });
72 } 82 }
73 } else if (!uid) { 83 } else if (!uid) {
74 res.json({ 84 res.json({
@@ -117,5 +127,6 @@ const collectShop = (req, res, next) => { @@ -117,5 +127,6 @@ const collectShop = (req, res, next) => {
117 module.exports = { 127 module.exports = {
118 changeFavoriteBrand, 128 changeFavoriteBrand,
119 collectProduct, 129 collectProduct,
120 - collectShop 130 + collectShop,
  131 + isFavoriteBrand
121 }; 132 };
1 -/**  
2 - * AbstractAction  
3 - *  
4 - * @author: Aiden Xu<aiden.xu@yoho.cn>  
5 - * @date: 2016/7/11  
6 - */  
7 -'use strict';  
8 -  
9 -const _ = require('lodash');  
10 -const Promise = require('bluebird');  
11 -  
12 -class AbstractAction {  
13 - constructor(req, res, next) {  
14 - if (!req || !res) {  
15 - throw new Error('Request and response object must be specified.');  
16 - }  
17 -  
18 - this.request = req;  
19 - this.response = res;  
20 - this.next = next;  
21 - this.renderContext = {};  
22 - }  
23 -  
24 - /**  
25 - * 判断是否是AJAX请求  
26 - *  
27 - * @return boolean 如果是AJAX请求返回 true  
28 - */  
29 -  
30 - /**  
31 - * 设置入口  
32 - *  
33 - * @param module 模块名称  
34 - * @param entry 入口名称  
35 - */  
36 - setEntry(module, entry) {  
37 - _.merge(this.renderContext, {  
38 - module: module,  
39 - page: entry  
40 - });  
41 - }  
42 -  
43 - /**  
44 - * 渲染视图  
45 - *  
46 - * @param template 模版名称  
47 - * @param context 上下文  
48 - */  
49 - renderTemplate(template, context) {  
50 - this.response.render(template, _.merge({}, this.renderContext, context));  
51 - }  
52 -  
53 - /**  
54 - * 内部渲染方法,该方法应该由 ActionCreator 来调用  
55 - *  
56 - * @returns Promise  
57 - */  
58 - _render() {  
59 - return this.render();  
60 - }  
61 -  
62 - /**  
63 - * 渲染回调方法  
64 - *  
65 - * @returns {Promise}  
66 - */  
67 - render() {  
68 - return new Promise(function(resolve) {  
69 - return resolve();  
70 - });  
71 - }  
72 -}  
73 -  
74 -module.exports = AbstractAction;  
1 -/**  
2 - * Actions  
3 - *  
4 - * @author: Aiden Xu<aiden.xu@yoho.cn>  
5 - * @date: 2016/7/11  
6 - */  
7 -'use strict';  
8 -  
9 -const AbstractAction = require('./abstract-action');  
10 -  
11 -/**  
12 - * 创建 Action  
13 - * @param Action  
14 - * @returns {Function}  
15 - */  
16 -const createAction = Action => {  
17 - return ((req, res, next) => {  
18 - const ret = new Action(req, res, next)._render();  
19 -  
20 - if (ret && typeof Promise.catch === 'function') {  
21 - ret.catch(next);  
22 - }  
23 - });  
24 -};  
25 -  
26 -module.exports = {  
27 - createAction,  
28 - AbstractAction  
29 -};  
1 -/**  
2 - * YohoAction  
3 - *  
4 - * @author: Aiden Xu<aiden.xu@yoho.cn>  
5 - * @date: 2016/7/11  
6 - */  
7 -'use strict';  
8 -  
9 -const CHANNEL_BOYS = 'boys';  
10 -const CHANNEL_GIRLS = 'girls';  
11 -const CHANNEL_KIDS = 'kids';  
12 -const CHANNEL_DEFAULT = CHANNEL_BOYS;  
13 -  
14 -module.exports = {  
15 - CHANNEL_DEFAULT,  
16 - CHANNEL_BOYS,  
17 - CHANNEL_GIRLS,  
18 - CHANNEL_KIDS  
19 -};  
1 -/**  
2 - * YohoAction  
3 - *  
4 - * @author: Aiden Xu<aiden.xu@yoho.cn>  
5 - * @date: 2016/7/11  
6 - */  
7 -'use strict';  
8 -  
9 -const AbstractAction = require('./abstract-action');  
10 -const Channel = require('./channel');  
11 -const moment = require('moment');  
12 -const COOKIE_DOMAIN = '.yohobuy.com';  
13 -const COOKIE_CHANEL_MAX_AGE = 300;  
14 -const _ = require('lodash');  
15 -  
16 -class YohoAction extends AbstractAction {  
17 -  
18 - /**  
19 - * 设置网站SEO的标题  
20 - *  
21 - * @param title 标题  
22 - * @param sign 连接的字符串  
23 - * @param showMore 是否显示更多内容  
24 - * @return void  
25 - */  
26 - setTitle(title, showMore, sign) {  
27 - showMore = showMore || true;  
28 - sign = sign || ' | ';  
29 -  
30 - _.merge(this.renderContext, {  
31 - title_more: showMore,  
32 - title: title + sign  
33 - });  
34 - }  
35 -  
36 - getUid() {  
37 - return this.request.user.uid;  
38 - }  
39 -  
40 - /**  
41 - * 获得当前用户所在的频道  
42 - *  
43 - * @returns {*}  
44 - */  
45 - getSessionChannel() {  
46 - const channel = this.request.cookies._Channel || Channel.CHANNEL_DEFAULT;  
47 -  
48 - if (!channel) {  
49 - // 设置默认频道  
50 - this.setSessionChannel(Channel.CHANNEL_BOYS);  
51 - }  
52 -  
53 - return channel;  
54 - }  
55 -  
56 - /**  
57 - * 设置当前用户的频道  
58 - */  
59 - setSessionChannel(channel) {  
60 - this.response.cookie('_Channel', channel || Channel.CHANNEL_DEFAULT, {  
61 - domain: COOKIE_DOMAIN,  
62 - maxAge: moment.duration(COOKIE_CHANEL_MAX_AGE, 'days').seconds()  
63 - });  
64 - }  
65 -  
66 - /**  
67 - * 根据用户访问的频道猜测用户性别  
68 - */  
69 - guessUserGender() {  
70 - switch (this.getSessionChannel()) {  
71 - case 'boys':  
72 - return '1,3';  
73 - case 'girls':  
74 - return '2,3';  
75 - default:  
76 - return '1,2,3';  
77 - }  
78 - }  
79 -  
80 - /**  
81 - * 设置网站SEO的关键词  
82 - *  
83 - * @param keywords 关键词,多个之间用","逗号分隔  
84 - */  
85 - setKeywords(keywords) {  
86 - // this->_view->assign('keywords', rtrim(keywords, ',') . ',');  
87 - _.merge(this.renderContext, {  
88 - keywords: keywords.replace(/~+/, '')  
89 - });  
90 - }  
91 -  
92 - /**  
93 - * 设置网站SEO的描述内容  
94 - *  
95 - * @param description 描述内容  
96 - * @param showMore 是否显示更多内容  
97 - * @param sign 连接的字符串  
98 - */  
99 - setDescription(description, showMore, sign) {  
100 - _.merge(this.renderContext, {  
101 - description_more: showMore,  
102 - description: description + sign  
103 - });  
104 - }  
105 -}  
106 -  
107 -module.exports = YohoAction;  
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 const Promise = require('bluebird'); 6 const Promise = require('bluebird');
7 const co = Promise.coroutine; 7 const co = Promise.coroutine;
8 const api = require('./brand-api'); 8 const api = require('./brand-api');
  9 +const _ = require('lodash');
9 10
10 const getBrandByDomainAsync = domain => { 11 const getBrandByDomainAsync = domain => {
11 return co(function*() { 12 return co(function*() {
@@ -24,7 +25,7 @@ const getBrandByDomainAsync = domain => { @@ -24,7 +25,7 @@ const getBrandByDomainAsync = domain => {
24 result.brandNameCn = brandInfo.data.brand_name_cn || ''; 25 result.brandNameCn = brandInfo.data.brand_name_cn || '';
25 result.brandAbout = brandInfo.data.brand_intro || ''; 26 result.brandAbout = brandInfo.data.brand_intro || '';
26 result.shopTemplateType = brandInfo.data.shop_template_type ? 27 result.shopTemplateType = brandInfo.data.shop_template_type ?
27 - parseInt(brandInfo.data.shop_template_type) : ''; 28 + _.parseInt(brandInfo.data.shop_template_type) : '';
28 result.type = brandInfo.data.type ? +brandInfo.data.type : 0; 29 result.type = brandInfo.data.type ? +brandInfo.data.type : 0;
29 result.shopId = brandInfo.data.shop_id || ''; 30 result.shopId = brandInfo.data.shop_id || '';
30 31
@@ -86,6 +86,23 @@ const getPromotionAsync = (skn) => { @@ -86,6 +86,23 @@ const getPromotionAsync = (skn) => {
86 return api.get('', params); 86 return api.get('', params);
87 }; 87 };
88 88
  89 +const getLimitedProductStatusAsync = (code, uid, skn) => {
  90 + let params = {
  91 + method: 'app.limitProduct.productStatus',
  92 + limitProductCode: code
  93 + };
  94 +
  95 + if (uid) {
  96 + params.uid = uid;
  97 + }
  98 +
  99 + if (skn) {
  100 + params.product_skn = skn;
  101 + }
  102 +
  103 + return api.get('', params);
  104 +};
  105 +
89 module.exports = { 106 module.exports = {
90 getProductBannerAsync, 107 getProductBannerAsync,
91 sizeInfoAsync, 108 sizeInfoAsync,
@@ -94,5 +111,6 @@ module.exports = { @@ -94,5 +111,6 @@ module.exports = {
94 getProductModelTryAsync, 111 getProductModelTryAsync,
95 getProductAsync, 112 getProductAsync,
96 getPromotionAsync, 113 getPromotionAsync,
97 - isSupportReturnedSale 114 + isSupportReturnedSale,
  115 + getLimitedProductStatusAsync
98 }; 116 };
@@ -35,16 +35,11 @@ const EXHIBITION_TICKET = 51335912; @@ -35,16 +35,11 @@ const EXHIBITION_TICKET = 51335912;
35 const _getProductAdditionInfoAsync = (data) => { 35 const _getProductAdditionInfoAsync = (data) => {
36 return co(function * () { 36 return co(function * () {
37 let productId = _.get(data, 'product_id', 0); 37 let productId = _.get(data, 'product_id', 0);
38 - let productSkn = _.get(data, 'product_skn', 0);  
39 let brandId = _.get(data, 'brand_info.brand_id', 0); 38 let brandId = _.get(data, 'brand_info.brand_id', 0);
40 39
41 // 获取相关数据 40 // 获取相关数据
42 let promiseData = { 41 let promiseData = {
43 productBanner: productAPI.getProductBannerAsync(productId), 42 productBanner: productAPI.getProductBannerAsync(productId),
44 - sizeInfo: productAPI.sizeInfoAsync(productSkn),  
45 - productComfort: productAPI.getProductComfortAsync(productId),  
46 - productModelCard: productAPI.getProductModelCardAsync(productId),  
47 - productModelTry: productAPI.getProductModelTryAsync(productSkn),  
48 bannerInfo: brandService.getBannerInfoAsync(brandId) 43 bannerInfo: brandService.getBannerInfoAsync(brandId)
49 }; 44 };
50 45
@@ -54,12 +49,25 @@ const _getProductAdditionInfoAsync = (data) => { @@ -54,12 +49,25 @@ const _getProductAdditionInfoAsync = (data) => {
54 })(); 49 })();
55 }; 50 };
56 51
  52 +const _getProductIntroAsync = (productId, productSkn) => {
  53 + return co(function * () {
  54 + let result = yield Promise.props({
  55 + sizeInfo: productAPI.sizeInfoAsync(productSkn),
  56 + productComfort: productAPI.getProductComfortAsync(productId),
  57 + productModelCard: productAPI.getProductModelCardAsync(productId),
  58 + productModelTry: productAPI.getProductModelTryAsync(productSkn)
  59 + });
  60 +
  61 + return result;
  62 + })();
  63 +};
  64 +
57 /** 65 /**
58 * 获取商品的喜欢 66 * 获取商品的喜欢
59 * pid : product id 67 * pid : product id
60 * bid : brand id 68 * bid : brand id
61 */ 69 */
62 -const _getProductFavoriteDataAsync = (uid, pid, bid) => { 70 +const _getProductFavoriteDataAsync = (uid, pid) => {
63 return co(function*() { 71 return co(function*() {
64 let result = { 72 let result = {
65 product: false, 73 product: false,
@@ -70,16 +78,18 @@ const _getProductFavoriteDataAsync = (uid, pid, bid) => { @@ -70,16 +78,18 @@ const _getProductFavoriteDataAsync = (uid, pid, bid) => {
70 return result; 78 return result;
71 } 79 }
72 80
73 - if (pid) {  
74 - let productData = yield favoriteProductService.isFavoriteAsync(uid, pid); 81 + let requestApi = {};
75 82
76 - result.product = productData.code === 200 && productData.data ? true : false; 83 + if (pid) {
  84 + requestApi.product = favoriteProductService.isFavoriteAsync(uid, pid);
77 } 85 }
78 86
79 - if (bid) {  
80 - let brandData = yield favoriteBrandService.isFavoriteAsync(uid, bid); 87 + let requestData = yield Promise.props(requestApi);
  88 +
  89 + let productData = requestData.product;
81 90
82 - result.brand = brandData.code && brandData.code === 200 ? true : false; 91 + if (productData) {
  92 + result.product = productData.code === 200 && productData.data ? true : false;
83 } 93 }
84 94
85 return result; 95 return result;
@@ -162,7 +172,7 @@ const _getVipDataByProductBaseInfo = (data, vipLevel, uid) => { @@ -162,7 +172,7 @@ const _getVipDataByProductBaseInfo = (data, vipLevel, uid) => {
162 const _getProductActivityBanner = (additionalData) => { 172 const _getProductActivityBanner = (additionalData) => {
163 let data = additionalData.productBanner; 173 let data = additionalData.productBanner;
164 174
165 - if (_.isElement(data) || _.get(data, 'code', 400) !== 200 || !_.get(data, 'data.bannerImg')) { 175 + if (_.isEmpty(data) || _.get(data, 'code', 400) !== 200 || !_.get(data, 'data.bannerImg')) {
166 return {}; 176 return {};
167 } 177 }
168 178
@@ -191,20 +201,10 @@ const _getActivityDataByProductBaseInfo = (data, additionalData) => { @@ -191,20 +201,10 @@ const _getActivityDataByProductBaseInfo = (data, additionalData) => {
191 }; 201 };
192 202
193 /** 203 /**
194 - * 获取商品咨询和评论数据  
195 - * @param data  
196 - */  
197 -const _getConsultCommentDataByProductInfo = () => {  
198 - return {  
199 - commentUrl: helpers.urlFormat('/home/comment')  
200 - };  
201 -};  
202 -  
203 -/**  
204 * 获取品牌数据 204 * 获取品牌数据
205 */ 205 */
206 const _getBrandDataByProductBaseInfo = (data, additionalData) => { 206 const _getBrandDataByProductBaseInfo = (data, additionalData) => {
207 - if (!data.brand_info) { 207 + if (!_.get(data, 'brand_info')) {
208 return {}; 208 return {};
209 } 209 }
210 210
@@ -212,7 +212,7 @@ const _getBrandDataByProductBaseInfo = (data, additionalData) => { @@ -212,7 +212,7 @@ const _getBrandDataByProductBaseInfo = (data, additionalData) => {
212 let bgImg = ''; 212 let bgImg = '';
213 let logo = ''; 213 let logo = '';
214 let bannerInfo = null; 214 let bannerInfo = null;
215 - let result = additionalData.bannerInfo; 215 + let result = _.get(additionalData, 'bannerInfo', {});
216 216
217 if (_.isEmpty(result)) { 217 if (_.isEmpty(result)) {
218 return {}; 218 return {};
@@ -236,7 +236,7 @@ const _getBrandDataByProductBaseInfo = (data, additionalData) => { @@ -236,7 +236,7 @@ const _getBrandDataByProductBaseInfo = (data, additionalData) => {
236 236
237 let homeUrl = 'javascript:void(0)'; // eslint-disable-line no-script-url 237 let homeUrl = 'javascript:void(0)'; // eslint-disable-line no-script-url
238 238
239 - if (data.brand_info.brand_domain) { 239 + if (_.get(data, 'brand_info.brand_domain')) {
240 homeUrl = helpers.urlFormat('', null, data.brand_info.brand_domain); 240 homeUrl = helpers.urlFormat('', null, data.brand_info.brand_domain);
241 } 241 }
242 242
@@ -262,7 +262,7 @@ const _getSkuDataByProductBaseInfo = (data) => { @@ -262,7 +262,7 @@ const _getSkuDataByProductBaseInfo = (data) => {
262 let defaultImage = '';// 默认图 262 let defaultImage = '';// 默认图
263 let chooseSkuFlag = false; // 选中状态 263 let chooseSkuFlag = false; // 选中状态
264 264
265 - if (!data.goods_list) { 265 + if (_.isEmpty(_.get(data, 'goods_list', []))) {
266 return { 266 return {
267 totalStorageNum, 267 totalStorageNum,
268 skuGoods, 268 skuGoods,
@@ -475,25 +475,17 @@ function _getSortNavAsync(smallSortId, gender) { @@ -475,25 +475,17 @@ function _getSortNavAsync(smallSortId, gender) {
475 } 475 }
476 476
477 // 保存在 gids 和 skns ,最近流览功能 477 // 保存在 gids 和 skns ,最近流览功能
478 -const saveRecentGoodInCookies = (oldGids, oldSkns, res, addGids, addSkns) => {  
479 - oldGids = (oldGids || '').split(',');  
480 - oldSkns = (oldSkns || '').split(',');  
481 - addSkns = `${addSkns}-${addGids}`; 478 +const saveRecentGoodInCookies = (oldSkns, res, addSkns) => {
482 479
483 - _.remove(oldGids, addGids);  
484 - _.remove(oldSkns, addSkns); 480 + oldSkns = oldSkns ? oldSkns.split(',') : [];
485 481
486 - oldGids.unshift(addGids);  
487 - oldSkns.unshift(addSkns); 482 + oldSkns = _.reject(oldSkns, old => old === String(addSkns) ? true : false);
488 483
489 - res.cookie('_browse', oldGids.splice(0, 30).join(','), {  
490 - maxAge: 2000000000,  
491 - domain: 'yohobuy.com'  
492 - }); 484 + oldSkns.unshift(addSkns);
493 485
494 res.cookie('_browseskn', oldSkns.splice(0, 30).join(','), { 486 res.cookie('_browseskn', oldSkns.splice(0, 30).join(','), {
495 maxAge: 2000000000, 487 maxAge: 2000000000,
496 - domain: 'yohobuy.com' 488 + domain: '.yohobuy.com'
497 }); 489 });
498 }; 490 };
499 491
@@ -948,7 +940,7 @@ const _getSizeAttrByMaxSortId = (maxSortId, sizeList) => { @@ -948,7 +940,7 @@ const _getSizeAttrByMaxSortId = (maxSortId, sizeList) => {
948 * @param maxSortId 940 * @param maxSortId
949 * @return object 941 * @return object
950 */ 942 */
951 -const _getSizeInfo = (productSkn, maxSortId, additionalData)=> { 943 +const _getIntroInfo = (productSkn, maxSortId, additionalData)=> {
952 if (!productSkn) { 944 if (!productSkn) {
953 return {}; 945 return {};
954 } 946 }
@@ -1004,17 +996,15 @@ const _getSizeInfo = (productSkn, maxSortId, additionalData)=> { @@ -1004,17 +996,15 @@ const _getSizeInfo = (productSkn, maxSortId, additionalData)=> {
1004 * @param array $navs 996 * @param array $navs
1005 * @return array 997 * @return array
1006 */ 998 */
1007 -const _getSeoByGoodsInfo = function(goodsInfo, navs) { 999 +const _getSeoByGoodsInfo = (goodsInfo, navs) => {
1008 let title = ''; 1000 let title = '';
1009 - let keywords = '';  
1010 let brandName = ''; 1001 let brandName = '';
1011 let sortName = ''; 1002 let sortName = '';
1012 - let description = '';  
1013 1003
1014 goodsInfo = goodsInfo || {}; 1004 goodsInfo = goodsInfo || {};
1015 navs = navs || []; 1005 navs = navs || [];
1016 1006
1017 - if (!_.isEmpty(goodsInfo.brandName)) { 1007 + if (goodsInfo.brandName) {
1018 title = goodsInfo.brandName + ' '; 1008 title = goodsInfo.brandName + ' ';
1019 brandName = goodsInfo.brandName; 1009 brandName = goodsInfo.brandName;
1020 } 1010 }
@@ -1025,9 +1015,9 @@ const _getSeoByGoodsInfo = function(goodsInfo, navs) { @@ -1025,9 +1015,9 @@ const _getSeoByGoodsInfo = function(goodsInfo, navs) {
1025 } 1015 }
1026 1016
1027 title += goodsInfo.name + '正品 '; 1017 title += goodsInfo.name + '正品 ';
1028 - keywords = brandName + sortName + ',' + brandName + '官网专卖店,' + brandName + '官方授权店,' + 1018 + let keywords = brandName + sortName + ',' + brandName + '官网专卖店,' + brandName + '官方授权店,' +
1029 brandName + '正品,' + brandName + '打折,' + brandName + '折扣店,' + brandName + '真品,' + brandName + '代购'; 1019 brandName + '正品,' + brandName + '打折,' + brandName + '折扣店,' + brandName + '真品,' + brandName + '代购';
1030 - description = !goodsInfo.shareDesc ? goodsInfo.name : goodsInfo.shareDesc; 1020 + let description = !goodsInfo.shareDesc ? goodsInfo.name : goodsInfo.shareDesc;
1031 1021
1032 return { 1022 return {
1033 title: title, 1023 title: title,
@@ -1041,12 +1031,22 @@ const _getSeoByGoodsInfo = function(goodsInfo, navs) { @@ -1041,12 +1031,22 @@ const _getSeoByGoodsInfo = function(goodsInfo, navs) {
1041 * @param origin Object 原始数据 1031 * @param origin Object 原始数据
1042 * @return result Object 格式化数据 1032 * @return result Object 格式化数据
1043 */ 1033 */
1044 -const _detailDataPkg = (origin, uid, vipLevel) => { 1034 +const _detailDataPkg = (origin, uid, vipLevel, cookies) => {
1045 return co(function*() { 1035 return co(function*() {
1046 - let result = {}; // 结果输出  
1047 - let md5 = origin.md5; 1036 + if (_.isEmpty(origin) || _.isEmpty(origin.data)) {
  1037 + return {};
  1038 + }
  1039 +
  1040 + let result = {};
  1041 +
  1042 + result.md5 = origin.md5;// 用于前端数据变化的对比
1048 1043
1049 origin = origin.data; 1044 origin = origin.data;
  1045 +
  1046 + if (uid) {
  1047 + origin.uid = uid;
  1048 + }
  1049 +
1050 let propOrigin = _.partial(_.get, origin); 1050 let propOrigin = _.partial(_.get, origin);
1051 1051
1052 // 商品名称 1052 // 商品名称
@@ -1054,34 +1054,51 @@ const _detailDataPkg = (origin, uid, vipLevel) => { @@ -1054,34 +1054,51 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
1054 return result; 1054 return result;
1055 } 1055 }
1056 1056
1057 - origin.uid = uid;  
1058 result.name = propOrigin('product_name'); 1057 result.name = propOrigin('product_name');
1059 result.skn = propOrigin('product_skn'); 1058 result.skn = propOrigin('product_skn');
1060 result.productId = propOrigin('product_id'); 1059 result.productId = propOrigin('product_id');
1061 - result.md5 = md5; // 用于前端数据变化的对比  
1062 1060
1063 result.maxSortId = propOrigin('maxSortId', ''); 1061 result.maxSortId = propOrigin('maxSortId', '');
1064 result.smallSortId = propOrigin('smallSortId', ''); 1062 result.smallSortId = propOrigin('smallSortId', '');
1065 1063
1066 result.goCartUrl = helpers.urlFormat('/shopping/cart'); 1064 result.goCartUrl = helpers.urlFormat('/shopping/cart');
1067 1065
1068 - let brandId = propOrigin('brand_info.brand_id', 0);  
1069 - let requestData = yield Promise.all([  
1070 - _getProductAdditionInfoAsync(origin), // 预处理所有的数据  
1071 - _getProductFavoriteDataAsync(uid, result.productId, brandId), // 处理收藏喜欢数据  
1072 - productAPI.getPromotionAsync(result.skn)  
1073 - ]); 1066 + let requestApi = {
  1067 + addition: _getProductAdditionInfoAsync(origin), // 预处理所有的数据
  1068 + fav: _getProductFavoriteDataAsync(uid, result.productId), // 处理收藏喜欢数据
  1069 + promotion: productAPI.getPromotionAsync(result.skn) // 打折信息
  1070 + };
  1071 +
  1072 + if (propOrigin('isLimitBuy', false) && propOrigin('limitProductCode', '')) {
  1073 + requestApi.limited = productAPI.getLimitedProductStatusAsync(
  1074 + propOrigin('limitProductCode'),
  1075 + uid,
  1076 + result.skn
  1077 + ); // 限购商品的状态
  1078 + }
  1079 +
  1080 + if (propOrigin('brand_info', '')) {
  1081 + requestApi.brand =
  1082 + brandService.getBrandByDomainAsync(propOrigin('brand_info.brand_domain')); // 品牌信息
  1083 + }
  1084 +
  1085 + let requestData = yield Promise.props(requestApi);
1074 1086
1075 - let additionalData = requestData[0];  
1076 - let favoriteData = requestData[1];  
1077 - let promotionData = requestData[2]; 1087 + let additionalData = requestData.addition;
  1088 + let favoriteData = requestData.fav;
  1089 + let promotionData = requestData.promotion;
  1090 + let limitedInfo = requestData.limited;
  1091 + let domainBrand = requestData.brand;
1078 1092
1079 // 商品标签 1093 // 商品标签
1080 result.tags = _getTagsDataByProductInfo(origin); 1094 result.tags = _getTagsDataByProductInfo(origin);
1081 1095
1082 - // 商品促销短语 1096 + // 商品促销短语
1083 result.saleTip = propOrigin('sales_phrase', ''); 1097 result.saleTip = propOrigin('sales_phrase', '');
1084 1098
  1099 + // 是否收藏
  1100 + result.isCollect = favoriteData.product;
  1101 +
1085 // 商品价格 1102 // 商品价格
1086 result.marketPrice = propOrigin('format_market_price'); 1103 result.marketPrice = propOrigin('format_market_price');
1087 result.salePrice = propOrigin('format_sales_price'); 1104 result.salePrice = propOrigin('format_sales_price');
@@ -1127,20 +1144,6 @@ const _detailDataPkg = (origin, uid, vipLevel) => { @@ -1127,20 +1144,6 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
1127 result.hasOtherPrice = false; 1144 result.hasOtherPrice = false;
1128 } 1145 }
1129 1146
1130 - // 商品咨询和评论数据,当前为空  
1131 - let consultComment = _getConsultCommentDataByProductInfo();  
1132 -  
1133 - // 品牌信息  
1134 - let banner = {};  
1135 -  
1136 - if (propOrigin('brand_info', '')) {  
1137 - result.brandImg = helpers.image(propOrigin('brand_info.brand_ico', ''), 47, 47);  
1138 - result.brandName = propOrigin('brand_info.brand_name', '');  
1139 - result.brandUrl = helpers.urlFormat('', null, propOrigin('brand_info.brand_domain'));  
1140 - banner = _getBrandDataByProductBaseInfo(origin, additionalData);  
1141 - banner.isCollect = favoriteData.brand;  
1142 - }  
1143 -  
1144 // sku商品信息 1147 // sku商品信息
1145 let skuData = _getSkuDataByProductBaseInfo(origin); 1148 let skuData = _getSkuDataByProductBaseInfo(origin);
1146 1149
@@ -1148,15 +1151,13 @@ const _detailDataPkg = (origin, uid, vipLevel) => { @@ -1148,15 +1151,13 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
1148 result.colors = skuData.skuGoods; 1151 result.colors = skuData.skuGoods;
1149 let totalStorageNum = skuData.totalStorageNum; 1152 let totalStorageNum = skuData.totalStorageNum;
1150 1153
1151 - // 是否收藏  
1152 - result.isCollect = favoriteData.product;  
1153 -  
1154 - if (propOrigin('isLimitBuy', false)) { 1154 + // 限购商品
  1155 + if (limitedInfo && limitedInfo.code === 200 && _.get(limitedInfo, 'data.isLimitBuy', false) === true) {
1155 // 是否开售 1156 // 是否开售
1156 - let isBeginSale = propOrigin('saleStatus', 0) === 1; 1157 + let isBeginSale = _.get(limitedInfo, 'data.saleStatus', 0) === 1;
1157 1158
1158 // 限购商品有关的展示状态 1159 // 限购商品有关的展示状态
1159 - let showStatus = propOrigin('showStatus', 1); 1160 + let showStatus = _.get(limitedInfo, 'data.showStatus', 1);
1160 1161
1161 let fashTopGoods = _getFashionTopGoodsStatus(uid, showStatus, isBeginSale); 1162 let fashTopGoods = _getFashionTopGoodsStatus(uid, showStatus, isBeginSale);
1162 1163
@@ -1177,6 +1178,7 @@ const _detailDataPkg = (origin, uid, vipLevel) => { @@ -1177,6 +1178,7 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
1177 } 1178 }
1178 } 1179 }
1179 1180
  1181 + // 商品购买状态
1180 let soldOut = !!(propOrigin('status') === 0 || totalStorageNum === 0); 1182 let soldOut = !!(propOrigin('status') === 0 || totalStorageNum === 0);
1181 let notForSale = propOrigin('attribute') === 2; // 非卖品 1183 let notForSale = propOrigin('attribute') === 2; // 非卖品
1182 let virtualGoods = propOrigin('attribute') === 3; // 虚拟商品 1184 let virtualGoods = propOrigin('attribute') === 3; // 虚拟商品
@@ -1244,44 +1246,62 @@ const _detailDataPkg = (origin, uid, vipLevel) => { @@ -1244,44 +1246,62 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
1244 statGoodsInfo.smallSortId = result.smallSortId; 1246 statGoodsInfo.smallSortId = result.smallSortId;
1245 statGoodsInfo.soldOut = soldOut ? 1 : 0; 1247 statGoodsInfo.soldOut = soldOut ? 1 : 0;
1246 1248
1247 - if (banner.brandId) {  
1248 - let domainBrand = yield brandService.getBrandByDomainAsync(banner.brandDomain); 1249 + // 商品的品牌信息
  1250 + let bandInfo = {};
  1251 +
  1252 + if (propOrigin('brand_info', '')) {
  1253 + result.brandImg = helpers.image(propOrigin('brand_info.brand_ico', ''), 47, 47);
  1254 + result.brandName = propOrigin('brand_info.brand_name', '');
  1255 + result.brandUrl = helpers.urlFormat('', null, propOrigin('brand_info.brand_domain'));
  1256 + bandInfo = _getBrandDataByProductBaseInfo(origin, additionalData);
  1257 + bandInfo.isCollect = favoriteData.brand;
1249 1258
1250 if (domainBrand.type && domainBrand.shopId) { 1259 if (domainBrand.type && domainBrand.shopId) {
1251 let type = _.parseInt(domainBrand.type); 1260 let type = _.parseInt(domainBrand.type);
1252 1261
1253 if (type === 1) { 1262 if (type === 1) {
1254 // 多品店不显示 1263 // 多品店不显示
1255 - banner = {}; 1264 + bandInfo = {};
1256 } else if (type === 2) { 1265 } else if (type === 2) {
1257 // 单品店显示新版的店铺banner 1266 // 单品店显示新版的店铺banner
1258 let basisData = yield shopService.basisTemplateAsync(domainBrand.shopId); 1267 let basisData = yield shopService.basisTemplateAsync(domainBrand.shopId);
1259 1268
1260 - banner.bgImg = basisData.shopTopBanner.banner || banner.bgImg; 1269 + bandInfo.bgImg = basisData.shopTopBanner.banner || bandInfo.bgImg;
1261 } 1270 }
1262 } 1271 }
1263 } 1272 }
1264 1273
  1274 + // 最近浏览功能 ,限量商品不加入到最近浏览
  1275 + if (!_.has(result, 'fashionTopGoods')) {
  1276 + cookies && cookies(_.get(result, 'skn', ''));
  1277 + }
1265 1278
1266 - // 获取商品尺寸相关  
1267 - let sizeInfo = _getSizeInfo(result, result.maxSortId, additionalData);  
1268 -  
1269 - return Object.assign({ 1279 + return {
1270 goodsInfo: result, 1280 goodsInfo: result,
1271 - consultComment: consultComment,  
1272 - banner: _.isEmpty(banner) ? null : banner, 1281 + banner: _.isEmpty(bandInfo) ? null : bandInfo,
1273 statGoodsInfo: statGoodsInfo 1282 statGoodsInfo: statGoodsInfo
1274 - }, sizeInfo); 1283 +
  1284 + };
1275 })(); 1285 })();
1276 }; 1286 };
1277 1287
1278 -const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5) => {  
1279 - let currentUserProductInfo = _.partial(_detailDataPkg, _, uid, vipLevel); 1288 +/**
  1289 + * 获得商品价格,活动等数据
  1290 + */
  1291 +const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5, cookie) => {
  1292 + let currentUserProductInfo = _.partial(_detailDataPkg, _, uid, vipLevel, cookie);
1280 1293
1281 return productAPI.getProductAsync(pid, uid, isStudent, vipLevel) 1294 return productAPI.getProductAsync(pid, uid, isStudent, vipLevel)
1282 - .then(result => currentUserProductInfo(result)) 1295 + .then(currentUserProductInfo)
1283 .then((result) => { 1296 .then((result) => {
1284 - if (result.goodsInfo.md5 !== dataMd5 || uid) { 1297 + if (_.isEmpty(result) || !_.get(result, 'goodsInfo.md5')) {
  1298 + return {
  1299 + code: 204, // 没有改变数据
  1300 + data: {}
  1301 + };
  1302 + }
  1303 +
  1304 + if (_.get(result, 'goodsInfo.md5') !== dataMd5 || uid) {
1285 return { 1305 return {
1286 code: 200, // 改变数据 1306 code: 200, // 改变数据
1287 data: result 1307 data: result
@@ -1296,60 +1316,81 @@ const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5) => { @@ -1296,60 +1316,81 @@ const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5) => {
1296 }; 1316 };
1297 1317
1298 /** 1318 /**
  1319 + * 是否支持退换货,true 支持,false 不支持
  1320 + */
  1321 +const saleReturn = (skn) => {
  1322 + return productAPI.isSupportReturnedSale(skn).then(result => _.get(result, `data.${skn}`, 'N') === 'N' ? 'Y' : 'N');
  1323 +};
  1324 +
  1325 +/**
1299 * 获取某一个商品详情主页面 1326 * 获取某一个商品详情主页面
1300 */ 1327 */
1301 const showMainAsync = (data) => { 1328 const showMainAsync = (data) => {
1302 return co(function * () { 1329 return co(function * () {
1303 - let result = {};  
1304 - let currentUserProductInfo = _.partial(_detailDataPkg, _, data.uid, data.vipLevel); 1330 + // 获取商品基本信息
  1331 + let productData = yield productAPI.getProductAsync(data.pid, data.uid, data.isStudent, data.vipLevel);
1305 1332
1306 - // 获取商品信息  
1307 - let productInfo = yield productAPI.getProductAsync(data.pid, data.uid, data.isStudent, data.vipLevel)  
1308 - .then(currentUserProductInfo);  
1309 -  
1310 - if (_.isEmpty(productInfo) || _.isEmpty(productInfo.goodsInfo)) { 1333 + if (_.isEmpty(productData.data)) {
1311 return Promise.reject({ 1334 return Promise.reject({
1312 - code: 404 1335 + code: 404,
  1336 + message: 'app.product.data api wrong'
1313 }); 1337 });
1314 } 1338 }
1315 1339
  1340 + let smallSortId = _.get(productData, 'data.smallSortId');
  1341 + let maxSortId = _.get(productData, 'data.maxSortId');
  1342 + let productId = _.get(productData, 'data.product_id');
  1343 + let productSkn = _.get(productData, 'data.product_skn');
  1344 + let curUserProduct = _.partial(_detailDataPkg, _, data.uid, data.vipLevel, data.gid, data.saveInCookies);
  1345 +
1316 let requestData = yield Promise.all([ 1346 let requestData = yield Promise.all([
1317 - _getSortNavAsync(productInfo.goodsInfo.smallSortId, data.gender),  
1318 - HeaderModel.requestHeaderData(data.channel),  
1319 - productAPI.isSupportReturnedSale(productInfo.goodsInfo.skn) 1347 + _getSortNavAsync(smallSortId, data.gender), // 面包屑导航
  1348 + HeaderModel.requestHeaderData(data.channel), // 通用头部数据
  1349 + _getProductIntroAsync(productId, productSkn), // 商品详细介绍
  1350 + curUserProduct(productData) // 商品详细价格
1320 ]); 1351 ]);
1321 1352
1322 - // 分类导航 ,seo  
1323 - let navs = requestData[0];  
1324 - let seo = _getSeoByGoodsInfo(productInfo.goodsInfo, navs);  
1325 -  
1326 - result.seo = seo;  
1327 -  
1328 - // 最近浏览功能 ,限量商品不能使用这个功能  
1329 - if (!_.has(productInfo, 'goodsInfo.fashionTopGoods')) {  
1330 - data.saveInCookies(data.gid, _.get(productInfo, 'goodsInfo.skn', ''));  
1331 - } 1353 + let smallSortNavigator = requestData[0];
  1354 + let navigatorHeader = requestData[1];
  1355 + let productDescription = requestData[2];
  1356 + let productInfo = requestData[3];
1332 1357
  1358 + // 拼装数据
  1359 + let result = {};
1333 1360
1334 - result.headerData = requestData[1].headerData; 1361 + // 商品价格
1335 result.productDetailPage = true; 1362 result.productDetailPage = true;
1336 result.detail = productInfo; 1363 result.detail = productInfo;
1337 - result.statGoodsInfo = Object.assign({fullSortName: navs.map(x => x.name).join('-')}, 1364 +
  1365 + // 商品介绍
  1366 + let intro = _getIntroInfo(productSkn, maxSortId, productDescription);
  1367 +
  1368 + result.deatil = Object.assign(result.detail, intro);
  1369 +
  1370 + // seo
  1371 + result.seo = _getSeoByGoodsInfo(productInfo.goodsInfo, smallSortNavigator);
  1372 +
  1373 + // 商品页面统计
  1374 + result.statGoodsInfo = Object.assign({fullSortName: smallSortNavigator.map(x => x.name).join('-')},
1338 productInfo.statGoodsInfo 1375 productInfo.statGoodsInfo
1339 ); 1376 );
1340 1377
1341 - // 是否支持退换货,true 支持,false 不支持  
1342 - result.detail.supportSaleReturnedService =  
1343 - _.get(requestData, `[2].data.${productInfo.goodsInfo.skn}`, 'N') === 'N';  
1344 -  
1345 - // 导航 1378 + // 面包屑导航
1346 result.detail.pathNav = _.concat( 1379 result.detail.pathNav = _.concat(
1347 homeService.getHomeChannelNav(data.channel), 1380 homeService.getHomeChannelNav(data.channel),
1348 - navs,  
1349 - [{name: productInfo.goodsInfo.name}] 1381 + smallSortNavigator,
  1382 + [{name: _.get(productInfo, 'goodsInfo.name')}]
1350 ); 1383 );
1351 1384
  1385 + // 头部数据
  1386 + result.headerData = navigatorHeader.headerData;
  1387 +
  1388 + // 咨询和评论
  1389 + result.detail.consultComment = true;
  1390 +
  1391 + // 最近浏览,最多5条记录
1352 result.detail.latestWalk = 5; 1392 result.detail.latestWalk = 5;
  1393 +
1353 return result; 1394 return result;
1354 })(); 1395 })();
1355 }; 1396 };
@@ -1362,5 +1403,6 @@ module.exports = { @@ -1362,5 +1403,6 @@ module.exports = {
1362 showMainAsync: showMainAsync, // 获取某一个商品详情主页面 1403 showMainAsync: showMainAsync, // 获取某一个商品详情主页面
1363 indexHotAreaAsync: hotAreaService.indexAsync, // 获取某一个商品的热区数据 1404 indexHotAreaAsync: hotAreaService.indexAsync, // 获取某一个商品的热区数据
1364 saveRecentGoodInCookies, // 保存最近的商品 1405 saveRecentGoodInCookies, // 保存最近的商品
1365 - getDetailHeader 1406 + getDetailHeader,
  1407 + saleReturn
1366 }; 1408 };
@@ -492,7 +492,7 @@ const getAdnav = (params) => { @@ -492,7 +492,7 @@ const getAdnav = (params) => {
492 492
493 if (result[1].code === 200 && result[1].data) { 493 if (result[1].code === 200 && result[1].data) {
494 dest.picTitle = brandFolderSeries; 494 dest.picTitle = brandFolderSeries;
495 - Object.assign(dest.list, searchHandler.handleFolderData(result[0].data)); 495 + Object.assign(dest.list, searchHandler.handleSeriesData(result[1].data));
496 } 496 }
497 497
498 498
@@ -516,6 +516,7 @@ const getShopInfo = (shopId, uid) => { @@ -516,6 +516,7 @@ const getShopInfo = (shopId, uid) => {
516 isFavorite: result.data.is_favorite === 'Y', 516 isFavorite: result.data.is_favorite === 'Y',
517 shopTemplateType: result.data.shop_template_type, 517 shopTemplateType: result.data.shop_template_type,
518 multBrandShopType: result.data.mult_brand_shop_type, 518 multBrandShopType: result.data.mult_brand_shop_type,
  519 + shopName: result.data.shop_name,
519 showShopName: result.data.is_show_shop_name === 'Y' 520 showShopName: result.data.is_show_shop_name === 'Y'
520 }; 521 };
521 } else { 522 } else {
@@ -549,7 +550,8 @@ const getShopData = (shopId, channel, params, shopInfo) => { @@ -549,7 +550,8 @@ const getShopData = (shopId, channel, params, shopInfo) => {
549 550
550 Object.assign(finalResult, 551 Object.assign(finalResult,
551 result[0], // 头部数据 552 result[0], // 头部数据
552 - searchHandler.handlePathNavData(shopInfo, params, 'shop', channel) // 面包屑导航 553 + searchHandler.handlePathNavData(shopInfo, params, 'shop', channel), // 面包屑导航
  554 + shopHandler.setShopSeo(shopInfo.shopName || shopInfo.brandName) // 店铺SEO
553 ); 555 );
554 556
555 _.set(finalResult, 'headerData.header', true); 557 _.set(finalResult, 'headerData.header', true);
@@ -786,6 +788,9 @@ const getShopListData = (channel, params, uid) => { @@ -786,6 +788,9 @@ const getShopListData = (channel, params, uid) => {
786 isFavorite: data.is_favorite === 'Y', 788 isFavorite: data.is_favorite === 'Y',
787 brandCont: data.shop_intro || '' 789 brandCont: data.shop_intro || ''
788 }); 790 });
  791 +
  792 + // 店铺SEO
  793 + Object.assign(finalResult, shopHandler.setShopSeo(data.shop_name));
789 } 794 }
790 } else { 795 } else {
791 return Promise.reject('No ShopDecorator data'); 796 return Promise.reject('No ShopDecorator data');
@@ -915,6 +920,9 @@ const getBaseShopData = (params, extra, channel, shopId) => { @@ -915,6 +920,9 @@ const getBaseShopData = (params, extra, channel, shopId) => {
915 name: shopName, 920 name: shopName,
916 pathTitle: shopName 921 pathTitle: shopName
917 }); 922 });
  923 +
  924 + // 店铺SEO
  925 + Object.assign(resData, shopHandler.setShopSeo(shopName));
918 } 926 }
919 927
920 // 临时删除seo信息 928 // 临时删除seo信息
@@ -731,9 +731,9 @@ exports.handlePagerData = (total, params) => { @@ -731,9 +731,9 @@ exports.handlePagerData = (total, params) => {
731 pages: [] 731 pages: []
732 }; 732 };
733 733
734 - let currentPage = parseInt((_.isEmpty(params.page) ? 1 : params.page), 10); // 当前页  
735 - let perPageCount = parseInt((_.isEmpty(params.limit) ? 60 : params.limit), 10); // 每页商品数  
736 - let totalPage = parseInt(total / perPageCount, 10) + 1; // 总页数 734 + let currentPage = parseInt(_.get(params, 'page', 1), 10); // 当前页
  735 + let perPageCount = parseInt(_.get(params, 'limit', 60), 10); // 每页商品数
  736 + let totalPage = Math.ceil(total / perPageCount); // 总页数
737 737
738 if (currentPage === 1) { 738 if (currentPage === 1) {
739 // 当前页为 1,一定没有上一页 739 // 当前页为 1,一定没有上一页
@@ -773,11 +773,14 @@ exports.handlePagerData = (total, params) => { @@ -773,11 +773,14 @@ exports.handlePagerData = (total, params) => {
773 } 773 }
774 } else if (currentPage > totalPage - 2) { 774 } else if (currentPage > totalPage - 2) {
775 for (let i = totalPage; i >= totalPage - 4; i--) { 775 for (let i = totalPage; i >= totalPage - 4; i--) {
776 - pages.push({  
777 - url: handleFilterUrl(params, {page: i}),  
778 - num: i,  
779 - cur: currentPage === i  
780 - }); 776 +
  777 + if (i > 0) {
  778 + pages.push({
  779 + url: handleFilterUrl(params, {page: i}),
  780 + num: i,
  781 + cur: currentPage === i
  782 + });
  783 + }
781 } 784 }
782 pages = _.sortBy(pages, ['num']); 785 pages = _.sortBy(pages, ['num']);
783 } 786 }
@@ -795,7 +798,7 @@ exports.handlePagerData = (total, params) => { @@ -795,7 +798,7 @@ exports.handlePagerData = (total, params) => {
795 num: '...' 798 num: '...'
796 }); 799 });
797 } 800 }
798 - if (currentPage < totalPage - 2) { 801 + if (currentPage < totalPage - 2 && totalPage > 5) {
799 nextPages.push({ 802 nextPages.push({
800 num: '...' 803 num: '...'
801 }); 804 });
1 /** 1 /**
2 * Created by TaoHuang on 2016/6/14. 2 * Created by TaoHuang on 2016/6/14.
3 */ 3 */
4 -  
5 -  
6 'use strict'; 4 'use strict';
  5 +const _ = require('lodash');
  6 +const md5 = require('md5');
7 7
8 -const api = global.yoho.SearchAPI;  
9 -  
10 -const yohoApi = global.yoho.API;  
11 - 8 +const api = global.yoho.API;
12 const serviceApi = global.yoho.ServiceAPI; 9 const serviceApi = global.yoho.ServiceAPI;
13 -const _ = require('lodash'); 10 +
14 const helpers = global.yoho.helpers; 11 const helpers = global.yoho.helpers;
15 -const images = require('../../../utils/images.js');  
16 const cache = global.yoho.cache; 12 const cache = global.yoho.cache;
17 const logger = global.yoho.logger; 13 const logger = global.yoho.logger;
18 -const md5 = require('md5');  
19 const config = require('../../../config/common'); 14 const config = require('../../../config/common');
  15 +const images = require('../../../utils/images.js');
20 16
21 const getSortByConditionAsync = (condition) => { 17 const getSortByConditionAsync = (condition) => {
22 - return api.get('sortgroup.json', condition); 18 + return api.get('', Object.assign({
  19 + method: 'web.regular.groupsort'
  20 + }, condition));
23 }; 21 };
24 22
25 // 判断用户是否收藏品牌 23 // 判断用户是否收藏品牌
@@ -40,12 +38,7 @@ const getSearchCackeKey = params => { @@ -40,12 +38,7 @@ const getSearchCackeKey = params => {
40 return md5(ks.join('_')); 38 return md5(ks.join('_'));
41 }; 39 };
42 40
43 -const getProductListOrig = (finalParams) => {  
44 -  
45 - return yohoApi.get('', finalParams).then(result => {  
46 - return result;  
47 - });  
48 -}; 41 +const getProductListOrig = finalParams => api.get('', finalParams);
49 42
50 /** 43 /**
51 * 获取商品列表 44 * 获取商品列表
@@ -104,11 +97,7 @@ const getProductList = (params) => { @@ -104,11 +97,7 @@ const getProductList = (params) => {
104 } 97 }
105 }; 98 };
106 99
107 -const getSortListOrig = (finalParams) => {  
108 - return yohoApi.get('', finalParams).then(ret => {  
109 - return ret;  
110 - });  
111 -}; 100 +const getSortListOrig = (finalParams) => api.get('', finalParams);
112 101
113 /** 102 /**
114 * 获取分类列表 103 * 获取分类列表
@@ -164,7 +153,7 @@ const getSortIntro = (params) => { @@ -164,7 +153,7 @@ const getSortIntro = (params) => {
164 }; 153 };
165 154
166 Object.assign(finalParams, params); 155 Object.assign(finalParams, params);
167 - return yohoApi.get('', finalParams); 156 + return api.get('', finalParams);
168 }; 157 };
169 158
170 /** 159 /**
@@ -177,7 +166,7 @@ const getSortAds = (params) => { @@ -177,7 +166,7 @@ const getSortAds = (params) => {
177 }; 166 };
178 167
179 Object.assign(finalParams, params); 168 Object.assign(finalParams, params);
180 - return yohoApi.get('', finalParams); 169 + return api.get('', finalParams);
181 }; 170 };
182 171
183 /** 172 /**
@@ -192,7 +181,7 @@ const getBrandSeries = (params) => { @@ -192,7 +181,7 @@ const getBrandSeries = (params) => {
192 status: params.status || 1 181 status: params.status || 1
193 }; 182 };
194 183
195 - return yohoApi.get('', finalParams); 184 + return api.get('', finalParams);
196 }; 185 };
197 186
198 /** 187 /**
@@ -207,7 +196,7 @@ const getBrandFolder = (params) => { @@ -207,7 +196,7 @@ const getBrandFolder = (params) => {
207 status: params.status || 1 196 status: params.status || 1
208 }; 197 };
209 198
210 - return yohoApi.get('', finalParams); 199 + return api.get('', finalParams);
211 }; 200 };
212 201
213 /** 202 /**
@@ -221,7 +210,7 @@ const getNodeContent = (params) => { @@ -221,7 +210,7 @@ const getNodeContent = (params) => {
221 node: params.node || '' 210 node: params.node || ''
222 }; 211 };
223 212
224 - return yohoApi.get('', finalParams); 213 + return api.get('', finalParams);
225 }; 214 };
226 215
227 /** 216 /**
@@ -235,7 +224,7 @@ const getWeekNew = (params) => { @@ -235,7 +224,7 @@ const getWeekNew = (params) => {
235 }; 224 };
236 225
237 Object.assign(finalParams, params); 226 Object.assign(finalParams, params);
238 - return yohoApi.get('', finalParams); 227 + return api.get('', finalParams);
239 }; 228 };
240 229
241 /** 230 /**
@@ -266,7 +255,7 @@ const getBrandShop = (query) => { @@ -266,7 +255,7 @@ const getBrandShop = (query) => {
266 if (retObj) { 255 if (retObj) {
267 return retObj; 256 return retObj;
268 } else { 257 } else {
269 - return yohoApi.get('', finalParams).then(ret => { 258 + return api.get('', finalParams).then(ret => {
270 if (ret && ret.code === 200) { 259 if (ret && ret.code === 200) {
271 260
272 cache.set(cKey, ret.data, 3600); 261 cache.set(cKey, ret.data, 3600);
@@ -304,7 +293,7 @@ const getShopsByBrandId = bid => { @@ -304,7 +293,7 @@ const getShopsByBrandId = bid => {
304 if (cdataObj) { 293 if (cdataObj) {
305 return cdataObj; 294 return cdataObj;
306 } else { 295 } else {
307 - return yohoApi.get('', finalParams).then(ret => { 296 + return api.get('', finalParams).then(ret => {
308 if (ret && ret.code === 200) { 297 if (ret && ret.code === 200) {
309 298
310 cache.set(cKey, ret.data, 3600); 299 cache.set(cKey, ret.data, 3600);
@@ -438,7 +427,7 @@ const getSuggest = (params) => { @@ -438,7 +427,7 @@ const getSuggest = (params) => {
438 keyword: params.keyword || '' 427 keyword: params.keyword || ''
439 }; 428 };
440 429
441 - return yohoApi.get('', finalParams); 430 + return api.get('', finalParams);
442 }; 431 };
443 432
444 433
@@ -452,7 +441,7 @@ const getBrandData = (params) => { @@ -452,7 +441,7 @@ const getBrandData = (params) => {
452 domain: params.domain || '' 441 domain: params.domain || ''
453 }; 442 };
454 443
455 - return yohoApi.get('', finalParams); 444 + return api.get('', finalParams);
456 }; 445 };
457 446
458 /** 447 /**
@@ -477,7 +466,7 @@ const getShopInfo = (shopId, uid) => { @@ -477,7 +466,7 @@ const getShopInfo = (shopId, uid) => {
477 uid: uid || 0 466 uid: uid || 0
478 }; 467 };
479 468
480 - return yohoApi.get('', finalParams); 469 + return api.get('', finalParams);
481 470
482 }; 471 };
483 472
@@ -485,15 +474,14 @@ const getShopInfo = (shopId, uid) => { @@ -485,15 +474,14 @@ const getShopInfo = (shopId, uid) => {
485 * 查询店铺下面的所有品牌 474 * 查询店铺下面的所有品牌
486 */ 475 */
487 const getShopBrands = (shopId) => { 476 const getShopBrands = (shopId) => {
488 -  
489 - return yohoApi.get('', {method: 'app.shops.getShopsBrands', shop_id: shopId || 0}); 477 + return api.get('', {method: 'app.shops.getShopsBrands', shop_id: shopId || 0});
490 }; 478 };
491 479
492 /** 480 /**
493 * 查询店铺装修 481 * 查询店铺装修
494 */ 482 */
495 const getShopDecorator = (shopId) => { 483 const getShopDecorator = (shopId) => {
496 - return yohoApi.get('', {method: 'app.shopsdecorator.getList', shop_id: shopId || 0}); 484 + return api.get('', {method: 'app.shopsdecorator.getList', shop_id: shopId || 0});
497 }; 485 };
498 486
499 /** 487 /**
@@ -515,7 +503,7 @@ const getBrands4Filter = (params) => { @@ -515,7 +503,7 @@ const getBrands4Filter = (params) => {
515 method: 'web.regular.aggBrand' 503 method: 'web.regular.aggBrand'
516 }; 504 };
517 505
518 - return yohoApi.get('', Object.assign({}, params, finalParams)); 506 + return api.get('', Object.assign(params, finalParams));
519 }; 507 };
520 508
521 module.exports = { 509 module.exports = {
@@ -1249,7 +1249,7 @@ exports.handleBrandBanner = (data) => { @@ -1249,7 +1249,7 @@ exports.handleBrandBanner = (data) => {
1249 }; 1249 };
1250 1250
1251 /** 1251 /**
1252 - * 处理品牌系列 1252 + * 处理品牌系列folder_id
1253 * @type {[type]} 1253 * @type {[type]}
1254 */ 1254 */
1255 exports.handleFolderData = (data) => { 1255 exports.handleFolderData = (data) => {
@@ -1266,6 +1266,23 @@ exports.handleFolderData = (data) => { @@ -1266,6 +1266,23 @@ exports.handleFolderData = (data) => {
1266 }; 1266 };
1267 1267
1268 /** 1268 /**
  1269 + * 处理品牌系列series
  1270 + * @type {[type]}
  1271 + */
  1272 +exports.handleSeriesData = (data) => {
  1273 + let dest = [];
  1274 +
  1275 + _.forEach(data, (value) => {
  1276 + dest.push({
  1277 + href: '?series=' + value.id,
  1278 + src: value.series_banner
  1279 + });
  1280 + });
  1281 +
  1282 + return dest;
  1283 +};
  1284 +
  1285 +/**
1269 * 筛选类链接处理的对外接口 1286 * 筛选类链接处理的对外接口
1270 * @type {[type]} 1287 * @type {[type]}
1271 */ 1288 */
@@ -16,6 +16,14 @@ const newProductsName = '新品上架 NEW'; @@ -16,6 +16,14 @@ const newProductsName = '新品上架 NEW';
16 const hotProductsName = '人气单品 HOT'; 16 const hotProductsName = '人气单品 HOT';
17 const shopListUrl = '/product/shoplist'; 17 const shopListUrl = '/product/shoplist';
18 18
  19 +exports.setShopSeo = (shopName) => {
  20 + return {
  21 + title: `${shopName} | ${shopName} 潮流服装服饰-Yoho!Buy有货`,
  22 + keywords: `${shopName},${shopName} 服装服饰,${shopName} 潮流服装服饰`,
  23 + description: `${shopName} | Yoho!Buy有货 ${shopName} 潮流服饰官方授权店!100%品牌正品保证,支持货到付款。`
  24 + };
  25 +};
  26 +
19 /** 27 /**
20 * 新品上架 28 * 新品上架
21 */ 29 */
@@ -59,6 +59,9 @@ router.post('/detail/consult', auth, detail.createConsult);// 创建咨询 @@ -59,6 +59,9 @@ router.post('/detail/consult', auth, detail.createConsult);// 创建咨询
59 router.get('/detail/hotarea', detail.indexHotArea);// 商品热区 59 router.get('/detail/hotarea', detail.indexHotArea);// 商品热区
60 router.post('/index/favoriteBrand', favorite.changeFavoriteBrand);// 收藏品牌 60 router.post('/index/favoriteBrand', favorite.changeFavoriteBrand);// 收藏品牌
61 router.post('/item/togglecollect', favorite.collectProduct); // 收藏商品 61 router.post('/item/togglecollect', favorite.collectProduct); // 收藏商品
  62 +router.get('/detail/header', detail.productHeader); // 价格数据重新获取接口
  63 +router.get('/detail/return', detail.detailReturn);// 特殊商品退换货
  64 +router.get('/index/isfav', favorite.isFavoriteBrand);// 品牌收藏状态
62 65
63 // 搜索 66 // 搜索
64 router.get('/search/index', search.index); 67 router.get('/search/index', search.index);
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 {{> product/standard-content}} 26 {{> product/standard-content}}
27 {{/if}} 27 {{/if}}
28 28
29 - {{> common/latest-walk}} 29 + {{> product/latest-walk}}
30 </div> 30 </div>
31 </div> 31 </div>
32 </div> 32 </div>
@@ -23,7 +23,8 @@ @@ -23,7 +23,8 @@
23 {{> common/path-nav}} 23 {{> common/path-nav}}
24 24
25 {{# goodsInfo}} 25 {{# goodsInfo}}
26 - <div class="main clearfix" data-skn="{{skn}}" data-id="{{productId}}"> 26 + <div class="main clearfix" data-skn="{{skn}}" data-id="{{productId}}" data-md5="{{md5}}"
  27 + data-skn="{{skn}}">
27 <div class="pull-left imgs clearfix"> 28 <div class="pull-left imgs clearfix">
28 <div class="pull-left img"> 29 <div class="pull-left img">
29 <div class="tags clearfix"> 30 <div class="tags clearfix">
@@ -540,12 +541,20 @@ @@ -540,12 +541,20 @@
540 <span class="title cur">商品详情 DETAILS</span> 541 <span class="title cur">商品详情 DETAILS</span>
541 </p> 542 </p>
542 <div id="details-html" class="details-html"> 543 <div id="details-html" class="details-html">
  544 + <div class="lazy-load-object">
  545 + <textarea class="datalazyload" style="visibility: hidden;">
  546 + <script>
  547 + fetchHotArea();
  548 + </script>
  549 + </textarea>
  550 + </div>
543 {{{details}}} 551 {{{details}}}
544 </div> 552 </div>
545 </div> 553 </div>
546 554
547 {{# consultComment}} 555 {{# consultComment}}
548 <div class="consult-comment info-block"> 556 <div class="consult-comment info-block">
  557 +
549 <p class="block-title"> 558 <p class="block-title">
550 <span class="title">顾客咨询(<em class="consult-num">0</em></span> 559 <span class="title">顾客咨询(<em class="consult-num">0</em></span>
551 <span class="sep">|</span> 560 <span class="sep">|</span>
@@ -561,7 +570,7 @@ @@ -561,7 +570,7 @@
561 </span> 570 </span>
562 </p> 571 </p>
563 <p class="btn-wrap"> 572 <p class="btn-wrap">
564 - <a class="btn" href="{{commentUrl}}" target="_blank"> 573 + <a class="btn" href="//www.yohobuy.com/home/comment" target="_blank">
565 <i class="iconfont">&#xe61e;</i> 574 <i class="iconfont">&#xe61e;</i>
566 我要评论 575 我要评论
567 </a> 576 </a>
@@ -599,6 +608,16 @@ @@ -599,6 +608,16 @@
599 </p> 608 </p>
600 </div> 609 </div>
601 </div> 610 </div>
  611 +
  612 + <div class="lazy-load-object">
  613 + <textarea class="datalazyload" style="visibility: hidden;">
  614 + <script>
  615 + fetchComment();
  616 + fetchReturn();
  617 + </script>
  618 + </textarea>
  619 + </div>
  620 +
602 </div> 621 </div>
603 {{/ consultComment}} 622 {{/ consultComment}}
604 623
@@ -635,11 +654,8 @@ @@ -635,11 +654,8 @@
635 </div> 654 </div>
636 </div> 655 </div>
637 656
638 - {{#if supportSaleReturnedService}}  
639 - <div class="support-saleReturned-service"></div>  
640 - {{^}}  
641 - <div class="not-support-saleReturned-service"></div>  
642 - {{/if}} 657 + <div id="saleReturn" class="support-saleReturned-service"></div>
  658 +
643 659
644 <div class="service"></div> 660 <div class="service"></div>
645 {{#if latestWalk}} 661 {{#if latestWalk}}
@@ -649,7 +665,7 @@ @@ -649,7 +665,7 @@
649 <span class="title cur">最近浏览 RECENT REVIEW</span> 665 <span class="title cur">最近浏览 RECENT REVIEW</span>
650 </p> 666 </p>
651 <div id="latest-walk-goods" class="goods clearfix"></div> 667 <div id="latest-walk-goods" class="goods clearfix"></div>
652 - {{> product/latest-walk}} 668 + {{> product/latest-walk-tpl}}
653 </div> 669 </div>
654 {{/if}} 670 {{/if}}
655 </div> 671 </div>
@@ -724,7 +740,8 @@ @@ -724,7 +740,8 @@
724 740
725 (function() { 741 (function() {
726 var mvl = document.createElement('script'); 742 var mvl = document.createElement('script');
727 - mvl.type = 'text/javascript'; mvl.async = true; 743 + mvl.type = 'text/javascript';
  744 + mvl.async = true;
728 mvl.src = ('https:' == document.location.protocol ? 'https://static-ssl.mediav.com/mvl.js' : 'http://static.mediav.com/mvl.js'); 745 mvl.src = ('https:' == document.location.protocol ? 'https://static-ssl.mediav.com/mvl.js' : 'http://static.mediav.com/mvl.js');
729 var s = document.getElementsByTagName('script')[0]; 746 var s = document.getElementsByTagName('script')[0];
730 s.parentNode.insertBefore(mvl, s); 747 s.parentNode.insertBefore(mvl, s);
@@ -732,13 +749,3 @@ @@ -732,13 +749,3 @@
732 </script> 749 </script>
733 {{/statGoodsInfo}} 750 {{/statGoodsInfo}}
734 751
735 -<script type="text/javascript">  
736 - (function() {  
737 - try {  
738 - var timestamp = (new Date()).valueOf();  
739 - var view = document.createElement('img');  
740 - view.src = 'http://shopping.yohobuy.com/1.jpg?t=' + timestamp;  
741 - } catch (e) {  
742 - }  
743 - })();  
744 -</script>  
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 <a name="stu-rights"> 18 <a name="stu-rights">
19 <h2 class="floor-title">学生权益</h2> 19 <h2 class="floor-title">学生权益</h2>
20 </a> 20 </a>
21 - <span id="rights-dia" class="floor-more">更多细则 ></span> 21 + <span id="rights-dia" class="floor-more">全部权益 ></span>
22 </div> 22 </div>
23 <div class="commodity-list clearfix"> 23 <div class="commodity-list clearfix">
24 {{> students/stu-rights}} 24 {{> students/stu-rights}}
1 -{{#if latestWalk}}  
2 - <input id="latest-walk-count" type="hidden" value="{{latestWalk}}">  
3 - <div class="latest-walk">  
4 - <h2>最近浏览的商品</h2>  
5 - <div id="latest-walk-goods" class="goods clearfix"></div>  
6 - </div>  
7 - {{> product/latest-walk-tpl}}  
8 -{{/if}}  
1 -<script id="latest-walk-tpl" type="text/html">  
2 - \{{# latestWalk}}  
3 - <div class="good">  
4 - <a href="\{{href}}" target="_blank">  
5 - <img class="lazy" data-original="\{{img}}">  
6 - </a>  
7 - <a class="name" href="\{{href}}" target="_blank">\{{name}}</a>  
8 - <p class="price">  
9 - <span class="market-price">\{{marketPrice}}</span>  
10 - <span class="sale-price">\{{salePrice}}</span>  
11 - </p>  
12 - </div>  
13 - \{{/ latestWalk}}  
14 -</script>  
1 -<div class="stu-rights clearfix"> 1 +<div id="stuRights" class="stu-rights clearfix">
2 {{#rightsItem}} 2 {{#rightsItem}}
3 <div class="stu-rights-item"> 3 <div class="stu-rights-item">
4 <img class="" src="{{image src 300 300}}"> 4 <img class="" src="{{image src 300 300}}">
5 </img> 5 </img>
6 </div> 6 </div>
7 {{/rightsItem}} 7 {{/rightsItem}}
8 -</div>  
  8 +</div>
@@ -36,8 +36,7 @@ module.exports = { @@ -36,8 +36,7 @@ module.exports = {
36 master: ['127.0.0.1:11211'], 36 master: ['127.0.0.1:11211'],
37 slave: ['127.0.0.1:11211'], 37 slave: ['127.0.0.1:11211'],
38 session: ['127.0.0.1:11211'], 38 session: ['127.0.0.1:11211'],
39 - reconnect: 5000,  
40 - timeout: 100, 39 + timeout: 1000,
41 retries: 0 40 retries: 0
42 }, 41 },
43 interfaceShunt: { 42 interfaceShunt: {
@@ -116,9 +115,8 @@ if (isProduction) { @@ -116,9 +115,8 @@ if (isProduction) {
116 master: ['127.0.0.1:12111'], 115 master: ['127.0.0.1:12111'],
117 slave: ['127.0.0.1:12112'], 116 slave: ['127.0.0.1:12112'],
118 session: ['127.0.0.1:12111'], 117 session: ['127.0.0.1:12111'],
119 - timeout: 100,  
120 - retries: 0,  
121 - reconnect: 5000 118 + timeout: 1000,
  119 + retries: 0
122 } 120 }
123 }); 121 });
124 } 122 }
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 module.exports = app => { 7 module.exports = app => {
8 8
9 // 公共服务 9 // 公共服务
  10 + app.use('/common', require('./apps/common'));
10 11
11 // 业务模块 12 // 业务模块
12 app.use(require('./apps/channel')); // 频道页 13 app.use(require('./apps/channel')); // 频道页
@@ -14,5 +15,7 @@ module.exports = app => { @@ -14,5 +15,7 @@ module.exports = app => {
14 app.use('/product', require('./apps/product')); // 商品相关页面 15 app.use('/product', require('./apps/product')); // 商品相关页面
15 app.use(require('./apps/passport')); // 登录注册 16 app.use(require('./apps/passport')); // 登录注册
16 app.use('/home', require('./apps/home')); // 会员中心 17 app.use('/home', require('./apps/home')); // 会员中心
  18 + app.use('/brands', require('./apps/brands'));
17 app.use('/guang', require('./apps/guang')); 19 app.use('/guang', require('./apps/guang'));
  20 + app.use('/cart', require('./apps/cart'));// 购物车
18 }; 21 };
@@ -52,10 +52,13 @@ const getNavBar = (data, type) => { @@ -52,10 +52,13 @@ const getNavBar = (data, type) => {
52 let obj = {}; 52 let obj = {};
53 let lowEn = _.camelCase(item.sort_name_en).toLowerCase(); 53 let lowEn = _.camelCase(item.sort_name_en).toLowerCase();
54 54
55 - obj.link = item.sort_url;  
56 - obj.cn = item.sort_name;  
57 - obj.en = item.sort_name_en;  
58 - obj.isNewPage = item.is_new_page === 'Y' ? true : false; 55 + Object.assign(obj, {
  56 + type: lowEn,
  57 + link: item.sort_url,
  58 + cn: item.sort_name,
  59 + en: item.sort_name_en,
  60 + isNewPage: item.is_new_page === 'Y'
  61 + });
59 62
60 if (type === lowEn) { 63 if (type === lowEn) {
61 obj.active = true; 64 obj.active = true;
@@ -135,30 +138,38 @@ const getThirdNav = (data) => { @@ -135,30 +138,38 @@ const getThirdNav = (data) => {
135 * @param {String} type 频道类型 138 * @param {String} type 频道类型
136 * @return {array} 子菜单数组 139 * @return {array} 子菜单数组
137 */ 140 */
138 -const getSubNav = (data, type) => {  
139 - let subNav = []; 141 +const getSubNavGroup = (data, type) => {
  142 + let subNavGroup = [];
140 143
141 _.forEach(data, it => { 144 _.forEach(data, it => {
142 - if (type === _.camelCase(it.sort_name_en).toLowerCase()) {  
143 - _.forEach(it.sub, item => {  
144 - let obj = {};  
145 -  
146 - obj.link = item.sort_url;  
147 - obj.name = item.sort_name;  
148 - obj.isHot = item.is_hot === 'Y' ? true : false;  
149 - obj.isNew = item.is_new === 'Y' ? true : false;  
150 -  
151 - if (item.sub) {  
152 - obj.thirdNav = getThirdNav(item.sub);  
153 - obj.imgCode = item.content_code;  
154 - }  
155 -  
156 - subNav.push(obj);  
157 - });  
158 - } 145 + let subNav = [];
  146 +
  147 + _.forEach(it.sub, item => {
  148 + let obj = {};
  149 +
  150 + obj.link = item.sort_url;
  151 + obj.name = item.sort_name;
  152 + obj.isHot = item.is_hot === 'Y' ? true : false;
  153 + obj.isNew = item.is_new === 'Y' ? true : false;
  154 +
  155 + if (item.sub) {
  156 + obj.thirdNav = getThirdNav(item.sub);
  157 + obj.imgCode = item.content_code;
  158 + }
  159 +
  160 + subNav.push(obj);
  161 + });
  162 +
  163 + let lowEn = _.camelCase(it.sort_name_en).toLowerCase();
  164 +
  165 + subNavGroup.push({
  166 + subType: lowEn,
  167 + subNav: subNav,
  168 + active: lowEn === type
  169 + });
159 }); 170 });
160 171
161 - return subNav; 172 + return subNavGroup;
162 }; 173 };
163 174
164 175
@@ -174,7 +185,7 @@ const setHeaderData = (resData, type) => ( @@ -174,7 +185,7 @@ const setHeaderData = (resData, type) => (
174 headType: type, 185 headType: type,
175 yohoGroup: getMenuData(), 186 yohoGroup: getMenuData(),
176 navbars: resData ? getNavBar(resData, type) : [], 187 navbars: resData ? getNavBar(resData, type) : [],
177 - subNav: resData ? getSubNav(resData, type) : [] 188 + subNavGroup: resData ? getSubNavGroup(resData, type) : []
178 } 189 }
179 ); 190 );
180 191
1 {{# headerData}} 1 {{# headerData}}
2 - <div class="yoho-header {{headType}}"> 2 + <div id="yoho-header" class="yoho-header" data-type="{{headType}}">
3 <div class="tool-wrapper clearfix"> 3 <div class="tool-wrapper clearfix">
4 <div class="center-content"> 4 <div class="center-content">
5 <div class="yoho-group-map left"> 5 <div class="yoho-group-map left">
@@ -19,8 +19,8 @@ @@ -19,8 +19,8 @@
19 <ul> 19 <ul>
20 <li id="loginBox"> 20 <li id="loginBox">
21 <span class="hi">Hi~</span> 21 <span class="hi">Hi~</span>
22 - [ <a href="//www.yohobuy.com/signin.html" class="loginbar">请登录</a> ]  
23 - [ <a href="//www.yohobuy.com/reg.html" class="registbar">免费注册</a> ] 22 + [ <a id="signin-url" href="//www.yohobuy.com/signin.html" class="loginbar">请登录</a> ]
  23 + [ <a id="reg-url" href="//www.yohobuy.com/reg.html" class="registbar">免费注册</a> ]
24 </li> 24 </li>
25 <li class="myyoho" id="myYohoBox"> 25 <li class="myyoho" id="myYohoBox">
26 <span class="tag-seprate"></span> 26 <span class="tag-seprate"></span>
@@ -66,7 +66,7 @@ @@ -66,7 +66,7 @@
66 <div class="main-logo"><a href="//www.yohobuy.com/" class="main-link"></a></div> 66 <div class="main-logo"><a href="//www.yohobuy.com/" class="main-link"></a></div>
67 <ul class="main-nav-list"> 67 <ul class="main-nav-list">
68 {{# navbars}} 68 {{# navbars}}
69 - <li {{#if active}} class="cure"{{/if}}{{#if ico}} style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}> 69 + <li class="{{type}}"{{#if ico}} style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}>
70 {{#if ico}} 70 {{#if ico}}
71 <a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"></a> 71 <a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"></a>
72 {{^}} 72 {{^}}
@@ -80,7 +80,7 @@ @@ -80,7 +80,7 @@
80 </li> 80 </li>
81 {{/ navbars}} 81 {{/ navbars}}
82 </ul> 82 </ul>
83 - <div class="func-area"> 83 + <div class="func-area hide">
84 <ul class="search-suggest"></ul> 84 <ul class="search-suggest"></ul>
85 <div class="search-2016"> 85 <div class="search-2016">
86 <form action="//search.yohobuy.com" method="get" id="search-form"> 86 <form action="//search.yohobuy.com" method="get" id="search-form">
@@ -108,7 +108,8 @@ @@ -108,7 +108,8 @@
108 </div> 108 </div>
109 <div class="nav-wrapper clearfix"> 109 <div class="nav-wrapper clearfix">
110 <div class="center-content"> 110 <div class="center-content">
111 - <ul class="sub-nav-list"> 111 + {{# subNavGroup}}
  112 + <ul class="sub-nav-list {{subType}}">
112 {{# subNav}} 113 {{# subNav}}
113 <li {{#if thirdNav}}class="contain-third"{{/if}}> 114 <li {{#if thirdNav}}class="contain-third"{{/if}}>
114 <a href="{{link}}">{{name}} 115 <a href="{{link}}">{{name}}
@@ -141,6 +142,7 @@ @@ -141,6 +142,7 @@
141 </li> 142 </li>
142 {{/ subNav}} 143 {{/ subNav}}
143 </ul> 144 </ul>
  145 + {{/ subNavGroup}}
144 </div> 146 </div>
145 </div> 147 </div>
146 </div> 148 </div>
@@ -272,4 +274,4 @@ @@ -272,4 +274,4 @@
272 </div> 274 </div>
273 275
274 <input id="api-domain" type="hidden" value="{{apiDomain}}"> 276 <input id="api-domain" type="hidden" value="{{apiDomain}}">
275 -{{/ headerData}}  
  277 +{{/ headerData}}
@@ -11,4 +11,9 @@ @@ -11,4 +11,9 @@
11 </p> 11 </p>
12 </div> 12 </div>
13 \{{/ latestWalk}} 13 \{{/ latestWalk}}
14 -</script>  
  14 +</script>
  15 +<div class="lazy-load-object">
  16 + <textarea class="latest-walk-datalazyload" style="visibility: hidden;">
  17 + <script> fetchLatestWalk(); </script>
  18 + </textarea>
  19 +</div>
@@ -4,5 +4,6 @@ @@ -4,5 +4,6 @@
4 <h2>最近浏览的商品</h2> 4 <h2>最近浏览的商品</h2>
5 <div id="latest-walk-goods" class="goods clearfix"></div> 5 <div id="latest-walk-goods" class="goods clearfix"></div>
6 </div> 6 </div>
  7 +
7 {{> product/latest-walk-tpl}} 8 {{> product/latest-walk-tpl}}
8 -{{/if}}  
  9 +{{/if}}
@@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
39 "express": "^4.13.1", 39 "express": "^4.13.1",
40 "handlebars": "^4.0.5", 40 "handlebars": "^4.0.5",
41 "express-handlebars": "^3.0.0", 41 "express-handlebars": "^3.0.0",
  42 + "express-session": "^1.13.0",
42 "influxdb-winston": "^1.0.1", 43 "influxdb-winston": "^1.0.1",
43 "lodash": "^4.13.1", 44 "lodash": "^4.13.1",
44 "md5": "^2.1.0", 45 "md5": "^2.1.0",
@@ -103,7 +104,6 @@ @@ -103,7 +104,6 @@
103 "yoho-handlebars": "^4.0.5", 104 "yoho-handlebars": "^4.0.5",
104 "yoho-jquery": "^1.12.4", 105 "yoho-jquery": "^1.12.4",
105 "yoho-jquery-lazyload": "^1.9.7", 106 "yoho-jquery-lazyload": "^1.9.7",
106 - "yoho-jquery-pjax": "0.0.1",  
107 "yoho-jquery-placeholder": "^2.3.1", 107 "yoho-jquery-placeholder": "^2.3.1",
108 "yoho-slider": "0.0.2", 108 "yoho-slider": "0.0.2",
109 "yoho-jquery-dotdotdot": "0.0.1", 109 "yoho-jquery-dotdotdot": "0.0.1",
  1 +/**
  2 + * 领券频道
  3 + * @author: 赵彪<bill.zhao@yoho.cn>
  4 + * @date: 2016/04/14
  5 + */
  6 +var $ = require('yoho-jquery'),
  7 + lazyLoad = require('yoho-jquery-lazyload'),
  8 + Dialog = require('../common/dialog').Dialog;
  9 +
  10 +
  11 +var alertConfig,
  12 + makeAlert;
  13 +
  14 +// j面跳转对象
  15 +var redirect = {
  16 +
  17 + // 去逛逛跳转链接
  18 + gunangSrc: null,
  19 +
  20 + // 查看优惠券跳转链接
  21 + checkCouponSrc: null,
  22 + goToGuang: function() {
  23 + window.location.href = this.gunangSrc;
  24 + },
  25 + goToCheck: function() {
  26 + window.location.href = this.checkCouponSrc;
  27 + },
  28 + 200: function(url) {
  29 + this.checkCouponSrc = url;
  30 + }
  31 +};
  32 +
  33 +require('../common');
  34 +require('../plugins/slider');
  35 +
  36 +// 加载底部图片
  37 +lazyLoad($('img.lazy'));
  38 +$('.slide-container').slider();
  39 +
  40 +// 根据配置执行展示弹窗
  41 +function couponAlert(opt) {
  42 + var newAlert = new Dialog(opt);
  43 +
  44 + newAlert.show();
  45 +}
  46 +
  47 +// 配置弹窗
  48 +alertConfig = {
  49 + success: {
  50 + content: '恭喜您,成功领取优惠券',
  51 + subContents: ['特殊情况下到账有延时', '请耐心等待'],
  52 + className: 'subcontent-dialog',
  53 + refreshOnClose: true,
  54 + btns: [{
  55 + id: 1,
  56 + name: '去购物啦',
  57 + btnClass: ['black', 'btn-close'],
  58 + cb: function() {
  59 + redirect.goToGuang();
  60 + }
  61 + }, {
  62 + id: 2,
  63 + name: '查看优惠券',
  64 + btnClass: ['btn-close'],
  65 + cb: function() {
  66 + redirect.goToCheck();
  67 + }
  68 + }]
  69 + },
  70 + alreadyGot: {
  71 + content: '您已领取过优惠券',
  72 + subContent: '快去选购心仪的潮品吧',
  73 + className: 'subcontent-dialog',
  74 + btns: [{
  75 + id: 1,
  76 + name: '去使用',
  77 + btnClass: ['btn-close'],
  78 + cb: function() {
  79 + redirect.goToGuang();
  80 + }
  81 + }]
  82 + },
  83 + expired: {
  84 + content: '优惠券已过期',
  85 + subContent: '去领最新的优惠券吧',
  86 + className: 'subcontent-dialog',
  87 + btns: [{
  88 + id: 1,
  89 + name: '关闭',
  90 + btnClass: ['btn-close']
  91 + }]
  92 + },
  93 + paramerror: {
  94 + content: '请求参数出错',
  95 + subContents: ['请检查参数后重试'],
  96 + className: 'subcontent-dialog',
  97 + btns: [{
  98 + id: 1,
  99 + name: '关闭',
  100 + btnClass: ['btn-close']
  101 + }]
  102 + },
  103 + failed: {
  104 + content: '领取失败',
  105 + subContents: ['请刷新重试,', '多次无效请联系客服'],
  106 + className: 'subcontent-dialog',
  107 + btns: [{
  108 + id: 1,
  109 + name: '刷新',
  110 + btnClass: ['btn-close'],
  111 + cb: function() {
  112 + window.location.reload();
  113 + }
  114 + }]
  115 + }
  116 +};
  117 +
  118 +// 对应不同的网络返回码展示不同的弹窗
  119 +makeAlert = {
  120 + 200: function() {
  121 + couponAlert(alertConfig.success);
  122 + },
  123 + 401: function() {
  124 + couponAlert(alertConfig.alreadyGot);
  125 + },
  126 + 315: function() {
  127 + couponAlert(alertConfig.expired);
  128 + },
  129 + 300: function() {
  130 + couponAlert(alertConfig.paramerror);
  131 + },
  132 + 500: function() {
  133 + couponAlert(alertConfig.failed);
  134 + }
  135 +};
  136 +
  137 +function requestCoupon(id) {
  138 + $.ajax({
  139 + type: 'GET',
  140 + url: '/coupon/sendcoupon',
  141 + data: {
  142 + id: id
  143 + },
  144 + success: function(res) {
  145 + var code = res.code;
  146 +
  147 + if (code === 400) {
  148 + window.location.href = res.data.refer; // 未登录情况下,跳转到登录页
  149 + return;
  150 + }
  151 +
  152 + // 如果返回的数据里有url,则执行redirect里的方法
  153 + res.url && res.url.length > 0 && redirect[code] && redirect[code](res.url + '?' + location.href.split('?')[1]);
  154 +
  155 + // 如果能找到对应code则执行相应方法,如果没有的对应code则执行error提示
  156 + makeAlert[code] ? makeAlert[code]() : makeAlert['500']();
  157 + },
  158 + error: function() {
  159 + var Alert = require('../common/dialog').Alert;
  160 +
  161 + new Alert('网络异常').show();
  162 + }
  163 + });
  164 +}
  165 +
  166 +function getCouponStatus() {
  167 + var hash,
  168 + data = {},
  169 + search = window.location.search,
  170 + hashes = search ? decodeURIComponent(search).slice(1).split('&') : [];
  171 +
  172 + for (i = 0; i < hashes.length; i++) {
  173 + hash = hashes[i].split('=');
  174 + data[hash[0]] = hash[1];
  175 + }
  176 +
  177 + if (!data.contentCode) {
  178 + return;
  179 + }
  180 +
  181 + $.ajax({
  182 + type: 'GET',
  183 + url: '/coupon/couponstatus',
  184 + data: data,
  185 + success: function(res) {
  186 + if (res.code === 200) {
  187 + var cates = res.categories || [];
  188 +
  189 + cates.forEach(function(obj) {
  190 + var e = document.getElementById(obj.id);
  191 + var child = e.children;
  192 +
  193 + if (!child.length) {
  194 + return;
  195 + }
  196 +
  197 + e.classList.remove('enable');
  198 +
  199 + for (var i = 0; i < child.length; i++) {
  200 + if (child[i].className === 'normal') {
  201 + child[i].classList.add('hidden');
  202 + }
  203 +
  204 + if ((obj.got && child[i].className.indexOf('got') > -1) || (obj.empty && child[i].className.indexOf('empty') > -1)) {
  205 + child[i].classList.remove('hidden');
  206 + }
  207 + }
  208 + })
  209 + }
  210 + }
  211 + });
  212 +}
  213 +
  214 +// 获取领券状态
  215 +getCouponStatus();
  216 +
  217 +$('.info').on('click', function(e) {
  218 + if (this.className.indexOf('enable') > -1) {
  219 + e.preventDefault();
  220 + requestCoupon($(this).closest('a').data('id'));
  221 + redirect.gunangSrc = $(this).closest('a').get(0).href;
  222 + }
  223 +});
  1 +/**
  2 + * 首页
  3 + * @author: xuqi<qi.xu@yoho.cn>
  4 + * @date: 2015/11/23
  5 + */
  6 +
  7 +var $ = require('yoho-jquery'),
  8 + lazyLoad = require('yoho-jquery-lazyload');
  9 +
  10 +var Handlebars = require('yoho-handlebars');
  11 +
  12 +var $tabs = $('.brands-tabs');
  13 +var $list = $('.brands-list');
  14 +var $gory = $('.brands-category');
  15 +var $news = $('.news-txt ul');
  16 +var $clearfix = $list.find('dl.clearfix');
  17 +var $brand = $list.find('li>a');
  18 +var $category = $gory.find('a');
  19 +var $tab = $tabs.find('li>a');
  20 +var $arr = $tabs.find('.hoverarr');
  21 +var $thisTab;
  22 +
  23 +var categoryHeight = $category.height();
  24 +var categoryTop = $category.offset() ? $category.offset().top : 0;
  25 +var newsHeight = $news.height();
  26 +var newsTop = $news.offset() ? $news.offset().top : 0;
  27 +var timeout, _id;
  28 +
  29 +
  30 +// 用于临时存储数据
  31 +var tempdata = {};
  32 +
  33 +var templete = '<div class="brands-dialog">';
  34 +
  35 +require('../common');
  36 +require('yoho-jquery-dotdotdot');
  37 +
  38 +templete += ' <div class="brands-layer">';
  39 +templete += ' <div class="layer-content">';
  40 +templete += ' <div class="title">{{title}}</div>';
  41 +templete += ' <div class="clearfix desc">';
  42 +templete += ' <img src="{{icon}}">';
  43 +templete += ' <p class="right">{{content}}</p>';
  44 +templete += ' </div> ';
  45 +templete += ' <div class="featured">';
  46 +templete += ' <p>{{subtitle}}</p> ';
  47 +templete += ' <div class="clearfix"> ';
  48 +templete += ' {{#each imgs}}';
  49 +templete += ' <img src="{{src}}">';
  50 +templete += ' {{/each}}';
  51 +templete += ' </div>';
  52 +templete += ' </div>';
  53 +templete += ' </div>';
  54 +templete += ' </div>';
  55 +templete += '</div>';
  56 +
  57 +$('.brand-desc').dotdotdot({
  58 + wrap: 'letter'
  59 +});
  60 +
  61 +
  62 +lazyLoad($('img.lazy'));
  63 +
  64 +// $('.slide-container').slider({
  65 +// orient: true
  66 +// });
  67 +
  68 +$.easing.easeOutQuint = function(x, t, b, c, d) {
  69 + return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
  70 +};
  71 +
  72 +function getQueryString(name) {
  73 + var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
  74 + var r = window.location.search.substr(1).match(reg);
  75 +
  76 + if (r !== null) {
  77 + return window.unescape(r[2]);
  78 + }
  79 + return null;
  80 +}
  81 +
  82 +// 格式化资讯NEWS标题数量
  83 +if ($news.length) {
  84 + $news.find('li').each(function() {
  85 + var $dom = $(this);
  86 + var domHeight = $dom.offset().top - newsTop + $dom.height();
  87 +
  88 + if (domHeight > newsHeight) {
  89 + $dom.hide();
  90 + }
  91 + });
  92 +}
  93 +
  94 +// 头部图片TAB切换展示
  95 +$tab.eq(0).parent('li').find('.brands-content').css('z-index', '1');
  96 +
  97 +_id = getQueryString('id') ? getQueryString('id') : 0;
  98 +
  99 +// 设置对应TAB选中
  100 +$thisTab = $tab.eq(_id);
  101 +$thisTab.find('.g-mask').addClass('g-mask-on');
  102 +
  103 +if (_id && $thisTab.length) {
  104 + $arr.css({
  105 + left: parseFloat($thisTab.offset().left) - parseFloat($tabs.offset().left)
  106 + });
  107 +}
  108 +$tab.hover(function() {
  109 + var $this = $(this);
  110 +
  111 + clearTimeout(timeout);
  112 + timeout = setTimeout(function() {
  113 + var targetLeft = parseFloat($this.offset().left) - parseFloat($tabs.offset().left);
  114 +
  115 + $arr.animate({
  116 + left: targetLeft
  117 + }, 200, 'easeOutQuint');
  118 + }, 50);
  119 + $tabs.find('.brands-content').removeAttr('style');
  120 + $this.parent('li').find('.brands-content').css('z-index', '1');
  121 +
  122 +}, function() {
  123 + clearTimeout(timeout);
  124 +});
  125 +
  126 +// 品牌类别滚动事件
  127 +$(window).scroll(function() {
  128 + if ($(this).scrollTop() >= categoryTop) {
  129 + $gory.addClass('category-fix');
  130 + } else {
  131 + $gory.removeClass('category-fix');
  132 + }
  133 +});
  134 +
  135 +// 点击字母,页面滚动到相关区域
  136 +$category.click(function() {
  137 + var name = $(this).attr('href').split('#')[1];
  138 + var targetTop = $list.find('[name="' + name + '"]').offset().top - categoryHeight;
  139 +
  140 + if (!$gory.hasClass('category-fix')) {
  141 + targetTop -= categoryHeight;
  142 + }
  143 +
  144 + $('html,body').animate({
  145 + scrollTop: targetTop
  146 + }, 200);
  147 + return false;
  148 +});
  149 +
  150 +
  151 +// 浮层代码
  152 +function bindTemplete($select, data, tmp) {
  153 + var $this = $select;
  154 + var offset = {
  155 + width: $this.width(),
  156 + left: $this.offset().left,
  157 + right: parseFloat($(window).width()) - parseFloat($this.offset().left) - parseFloat($this.width())
  158 + };
  159 +
  160 + var $parent = $this.parent('li');
  161 + var myTemplate;
  162 +
  163 + $list.find('.brands-dialog').remove();
  164 + myTemplate = Handlebars.compile(tmp);
  165 + $parent.append(myTemplate(data));
  166 + $parent.find('.right').dotdotdot({
  167 + wrap: 'letter'
  168 + });
  169 +
  170 + if (offset.right - 350 < 0) {
  171 + $parent.find('.brands-layer')
  172 + .addClass('brands-layer-right').css('left', -330 - offset.width);
  173 + }
  174 +}
  175 +
  176 +// 鼠标悬浮品牌,请求数据,并且展示
  177 +function bindHoverEvent() {
  178 + $brand.unbind('mouseenter').unbind('mouseleave').hover(function() {
  179 + var $this = $(this);
  180 + var key = $this.attr('data-key');
  181 +
  182 + var options = {
  183 + url: '/brands/brandinfo',
  184 + type: 'get',
  185 + data: {
  186 + brandId: key
  187 + },
  188 + success: function(_data) {
  189 +
  190 + if (_data.code === 200 && _data.brand) {
  191 + if (!tempdata.hasOwnProperty(_data.brand.key)) {
  192 + tempdata[_data.brand.key] = _data.brand;
  193 + }
  194 + bindTemplete($this, tempdata[_data.brand.key], templete);
  195 + }
  196 + }
  197 + };
  198 +
  199 + clearTimeout(timeout);
  200 + timeout = setTimeout(function() {
  201 + if (!tempdata.hasOwnProperty(key)) {
  202 + $.ajax(options);
  203 + } else {
  204 + bindTemplete($this, tempdata[key], templete);
  205 + }
  206 + }, 200);
  207 + }, function() {
  208 + clearTimeout(timeout);
  209 + $list.find('.brands-dialog').remove();
  210 + });
  211 +}
  212 +
  213 +if ($clearfix.length < 26) {
  214 + $.ajax({
  215 + url: '/brands/brandList',
  216 + type: 'POST',
  217 + data: {
  218 + start: $clearfix.length ? ($clearfix.length + 1) : 1
  219 + },
  220 + success: function(_data) {
  221 + if (_data) {
  222 + $list.append(_data);
  223 + $brand = $list.find('li>a');
  224 + bindHoverEvent();
  225 + }
  226 + }
  227 + });
  228 +}
  229 +
  230 +bindHoverEvent();
@@ -9,6 +9,9 @@ var $ = require('yoho-jquery'); @@ -9,6 +9,9 @@ var $ = require('yoho-jquery');
9 var homePage = $('.home-page').data('page'), 9 var homePage = $('.home-page').data('page'),
10 brandUrl = $('.logo-brand').data('url'); 10 brandUrl = $('.logo-brand').data('url');
11 11
  12 +// 给头部js获取当前频道
  13 +window.homePage = homePage;
  14 +
12 require('../common'); 15 require('../common');
13 16
14 require('../plugins/slider'); 17 require('../plugins/slider');
@@ -74,6 +74,16 @@ function getUid() { @@ -74,6 +74,16 @@ function getUid() {
74 return user[1]; 74 return user[1];
75 } 75 }
76 76
  77 +function getProfileName() {
  78 + var user = getUser();
  79 +
  80 + if (user === 0) {
  81 + return 0;
  82 + }
  83 +
  84 + return user[0];
  85 +}
  86 +
77 function getShoppingKey() { 87 function getShoppingKey() {
78 var c = cookie('_g'); 88 var c = cookie('_g');
79 89
@@ -132,16 +142,19 @@ $(window).on('resize', function() { @@ -132,16 +142,19 @@ $(window).on('resize', function() {
132 } 142 }
133 }).trigger('resize'); 143 }).trigger('resize');
134 144
  145 +// 对特殊的情况进行处理
135 function queryString() { 146 function queryString() {
136 var vars = {}, 147 var vars = {},
137 hash, 148 hash,
  149 + index,
138 i, 150 i,
139 search = window.location.search, 151 search = window.location.search,
140 hashes = search ? decodeURIComponent(search).slice(1).split('&') : []; 152 hashes = search ? decodeURIComponent(search).slice(1).split('&') : [];
141 153
142 for (i = 0; i < hashes.length; i++) { 154 for (i = 0; i < hashes.length; i++) {
143 - hash = hashes[i].split('=');  
144 - vars[hash[0]] = hash[1]; 155 + index = hashes[i].indexOf('=');
  156 + hash = hashes[i].substring(0, index);
  157 + vars[hash] = hashes[i].substring(++index);
145 } 158 }
146 return vars; 159 return vars;
147 } 160 }
@@ -174,6 +187,8 @@ window.getUser = getUser; @@ -174,6 +187,8 @@ window.getUser = getUser;
174 187
175 window.getUid = getUid; 188 window.getUid = getUid;
176 189
  190 +window.getProfileName = getProfileName;
  191 +
177 window.getShoppingKey = getShoppingKey; 192 window.getShoppingKey = getShoppingKey;
178 193
179 window.queryString = queryString; 194 window.queryString = queryString;
@@ -15,10 +15,13 @@ var $head = $('.head-wrapper'), @@ -15,10 +15,13 @@ var $head = $('.head-wrapper'),
15 $logotrans = $head.find('.main-logo'), 15 $logotrans = $head.find('.main-logo'),
16 $searchSug = $head.find('.search-suggest'), 16 $searchSug = $head.find('.search-suggest'),
17 $goCart = $head.find('.go-cart'), 17 $goCart = $head.find('.go-cart'),
  18 + $myYohoBox = $('#myYohoBox'),
18 $goodsNum = $goCart.find('.goods-num-tip'), 19 $goodsNum = $goCart.find('.goods-num-tip'),
19 $miniCart = $head.find('.mini-cart-wrapper'); 20 $miniCart = $head.find('.mini-cart-wrapper');
20 21
21 -var $subNav = $('.sub-nav-list .contain-third'); 22 +var $subNav = $('.sub-nav-list.cure .contain-third');
  23 +
  24 +var fetchUserInfoEvent = $.Callbacks('once'); // eslint-disable-line
22 25
23 var thirdLineNum = 9, 26 var thirdLineNum = 9,
24 delayer, 27 delayer,
@@ -72,6 +75,12 @@ var bannerMap = { @@ -72,6 +75,12 @@ var bannerMap = {
72 }, 75 },
73 cookieMap = {}; 76 cookieMap = {};
74 77
  78 +var $signinBtn = $('#signin-url');
  79 +var $regBtn = $('#reg-url');
  80 +
  81 +$signinBtn.attr('href', '//www.yohobuy.com/signin.html?refer=' + window.location.href);
  82 +$regBtn.attr('href', '//www.yohobuy.com/reg.html?refer=' + window.location.href);
  83 +
75 // handlebars模板 84 // handlebars模板
76 centerFn = handlebars.compile($('#simple-account-info-tpl').html() || ''); 85 centerFn = handlebars.compile($('#simple-account-info-tpl').html() || '');
77 loginFn = handlebars.compile($('#header-login-info-tpl').html() || ''); 86 loginFn = handlebars.compile($('#header-login-info-tpl').html() || '');
@@ -111,16 +120,16 @@ function setTopBanner(data) { @@ -111,16 +120,16 @@ function setTopBanner(data) {
111 } else { 120 } else {
112 topbanner = '<div class="yoho-notice">' + 121 topbanner = '<div class="yoho-notice">' +
113 '<div class="notice-container center-content">' + 122 '<div class="notice-container center-content">' +
114 - '<h1 class="notice-title">关于系统升级的公告</h1>' +  
115 - '<div class="notice-content">' +  
116 - '<p class="tips">尊敬的顾客:</p>' +  
117 - '<p class="detail">您好!为了向您提供更优质的服务,目前系统正在升级,请耐心等待。</p>' +  
118 - '<p class="detail">系统升级期间,部分地区用户体验会有暂时中断,如遇紧急事宜,欢迎垂询客服热线:' +  
119 - '400-889-9646 09:00-22:30(周一至周日)。稍后系统将恢复正常' +  
120 - '使用,欢迎您继续光顾YOHO!BUY有货!带来不便之处深表歉意,请您谅解!</p>' +  
121 - '</div>' + 123 + '<h1 class="notice-title">关于系统升级的公告</h1>' +
  124 + '<div class="notice-content">' +
  125 + '<p class="tips">尊敬的顾客:</p>' +
  126 + '<p class="detail">您好!为了向您提供更优质的服务,目前系统正在升级,请耐心等待。</p>' +
  127 + '<p class="detail">系统升级期间,部分地区用户体验会有暂时中断,如遇紧急事宜,欢迎垂询客服热线:' +
  128 + '400-889-9646 09:00-22:30(周一至周日)。稍后系统将恢复正常' +
  129 + '使用,欢迎您继续光顾YOHO!BUY有货!带来不便之处深表歉意,请您谅解!</p>' +
122 '</div>' + 130 '</div>' +
123 - '</div>'; 131 + '</div>' +
  132 + '</div>';
124 } 133 }
125 $('body').prepend(topbanner); 134 $('body').prepend(topbanner);
126 } 135 }
@@ -239,18 +248,11 @@ function formatThirdMenu() { @@ -239,18 +248,11 @@ function formatThirdMenu() {
239 248
240 // 更新头部登陆信息 249 // 更新头部登陆信息
241 function updateLoginInfo(data) { 250 function updateLoginInfo(data) {
242 - var info = {  
243 - usercenter: '//www.yohobuy.com/home?t=' + new Date().getTime(),  
244 - nickname: data.profileName,  
245 - signout: '//www.yohobuy.com/logout.html'  
246 - };  
247 -  
248 if (data.curLevel * 1 === 3) { 251 if (data.curLevel * 1 === 3) {
249 data.vip3 = true; 252 data.vip3 = true;
250 } 253 }
251 254
252 $tool.find('.simple-user-center').html(centerFn(data)); 255 $tool.find('.simple-user-center').html(centerFn(data));
253 - $loginBox.html(loginFn(info));  
254 } 256 }
255 257
256 // 同步sso登录状态 258 // 同步sso登录状态
@@ -260,7 +262,7 @@ function syncLoginInfo() { @@ -260,7 +262,7 @@ function syncLoginInfo() {
260 method: 'open.passport.get' 262 method: 'open.passport.get'
261 }; 263 };
262 264
263 - $.getJSON('//www.yohobuy.com/common/passport/?callback=?', param, function(jsonData) { 265 + return $.getJSON('//www.yohobuy.com/common/passport/?callback=?', param, function(jsonData) {
264 if (jsonData && jsonData.data && jsonData.data.result !== -1) { 266 if (jsonData && jsonData.data && jsonData.data.result !== -1) {
265 updateLoginInfo(jsonData.data); 267 updateLoginInfo(jsonData.data);
266 } else { 268 } else {
@@ -269,7 +271,6 @@ function syncLoginInfo() { @@ -269,7 +271,6 @@ function syncLoginInfo() {
269 expires: -1 271 expires: -1
270 }); 272 });
271 } 273 }
272 - $loginBox.show();  
273 }); 274 });
274 } 275 }
275 276
@@ -481,7 +482,35 @@ function isSupportCss3Animation() { @@ -481,7 +482,35 @@ function isSupportCss3Animation() {
481 return false; 482 return false;
482 } 483 }
483 } 484 }
  485 +}
  486 +
  487 +// 处理pageCache频道显示异常问题
  488 +function syncPageChannel() {
  489 + var $header = $('#yoho-header'),
  490 + $navs;
  491 + var channel = window.homePage || window.cookie('_Channel') || 'boys',
  492 + qs = window.queryString();
  493 +
  494 + channel = qs.channel ? qs.channel : channel;
484 495
  496 + if ($header && $header.length) {
  497 + $navs = $header.find('.' + channel);
  498 +
  499 + if (!$navs.length) {
  500 + channel = 'boys';
  501 + $navs = $header.find('.' + channel);
  502 + }
  503 +
  504 + // 更新频道菜单选中状态
  505 + $navs.siblings('.cure').removeClass('cure');
  506 + $navs.addClass('cure');
  507 +
  508 + // 更新频道颜色
  509 + $header.addClass(channel).find('.func-area').removeClass('hide');
  510 +
  511 + // 更新三级菜单jq对象
  512 + $subNav = $('.sub-nav-list.cure .contain-third');
  513 + }
485 } 514 }
486 515
487 if (isSupportCss3Animation()) { 516 if (isSupportCss3Animation()) {
@@ -490,11 +519,45 @@ if (isSupportCss3Animation()) { @@ -490,11 +519,45 @@ if (isSupportCss3Animation()) {
490 } else { 519 } else {
491 window.setTimeout(fadeAnimate, 3000); 520 window.setTimeout(fadeAnimate, 3000);
492 } 521 }
  522 +syncPageChannel();
493 getBannerAndNotice(); // 获取头部banner 523 getBannerAndNotice(); // 获取头部banner
494 -syncLoginInfo(); // 同步登陆信息  
495 formatThirdMenu(); // 格式化三级菜单 524 formatThirdMenu(); // 格式化三级菜单
496 setInterval(syncCratInfo, 2000); // 定时同步购物车数量 525 setInterval(syncCratInfo, 2000); // 定时同步购物车数量
497 526
  527 +// 获取头部登陆信息
  528 +(function() {
  529 + var uid = getUid(), //eslint-disable-line
  530 + profileName = getProfileName(); // eslint-disable-line
  531 +
  532 + var info = {
  533 + usercenter: '//www.yohobuy.com/home?t=' + new Date().getTime(),
  534 + nickname: profileName,
  535 + signout: '//www.yohobuy.com/logout.html'
  536 + };
  537 +
  538 + if (uid !== 0) {
  539 + $loginBox.html(loginFn(info));
  540 + }
  541 +
  542 + $loginBox.show();
  543 +}());
  544 +
  545 +fetchUserInfoEvent.add(syncLoginInfo);
  546 +
  547 +$myYohoBox.hover(function() {
  548 + var uid = getUid(); // eslint-disable-line
  549 +
  550 + $myYohoBox.addClass('myyoho-hover');
  551 +
  552 + if (uid === 0) {
  553 + return;
  554 + }
  555 +
  556 + fetchUserInfoEvent.fire();
  557 +}, function() {
  558 + $myYohoBox.removeClass('myyoho-hover');
  559 +});
  560 +
498 $yohoGroup.hover(function() { 561 $yohoGroup.hover(function() {
499 var data = $(this).data(); 562 var data = $(this).data();
500 563
1 -require('./login/index');  
2 require('../common'); 1 require('../common');
  2 +require('./login/index');
@@ -912,6 +912,26 @@ function authUntilThreeTimesAsync() { @@ -912,6 +912,26 @@ function authUntilThreeTimesAsync() {
912 }); 912 });
913 } 913 }
914 914
  915 +// 设置 refer 信息
  916 +function setRefer() {
  917 + var refer = queryString().refer || '', // eslint-disable-line
  918 + regUrl;
  919 +
  920 + if (refer) {
  921 + setCookie('refer', refer, {domain: '.yohobuy.com', path: '/'}); // eslint-disable-line
  922 + }
  923 +
  924 + regUrl = (function() {
  925 + if (refer) {
  926 + return '/reg.html?refer=' + refer;
  927 + } else {
  928 + return '/reg.html';
  929 + }
  930 + }());
  931 +
  932 + $('.fast-reg').attr('href', regUrl);
  933 +}
  934 +
915 /** ************************************************************************/ 935 /** ************************************************************************/
916 /* 事件绑定 */ 936 /* 事件绑定 */
917 /** ************************************************************************/ 937 /** ************************************************************************/
@@ -948,7 +968,7 @@ $(document).on('click', function(e) { @@ -948,7 +968,7 @@ $(document).on('click', function(e) {
948 968
949 /** ************************************************************************/ 969 /** ************************************************************************/
950 970
951 - // 邮箱自动完成后失去焦点:仅进行本地格式验证格式; 971 + // 邮箱自动完成后失去焦点:仅进行本地格式验证格式;
952 mailAc($accountInput1, function() { 972 mailAc($accountInput1, function() {
953 return currentLogin.validateAccountLocal(); 973 return currentLogin.validateAccountLocal();
954 }); 974 });
@@ -1128,6 +1148,9 @@ if (($accountInput1.val() !== '' || @@ -1128,6 +1148,9 @@ if (($accountInput1.val() !== '' ||
1128 $passwordInput.focus(); 1148 $passwordInput.focus();
1129 } 1149 }
1130 1150
  1151 +// 设置 refer
  1152 +setRefer();
  1153 +
1131 /** ************************************************************************/ 1154 /** ************************************************************************/
1132 /* 结束 */ 1155 /* 结束 */
1133 /** ************************************************************************/ 1156 /** ************************************************************************/
1 var reg = require('./reg/reg'); 1 var reg = require('./reg/reg');
2 2
3 -reg.init('reg');  
4 require('../common'); 3 require('../common');
  4 +reg.init('reg');
@@ -252,7 +252,6 @@ function validateRule(page, $element, callback) { @@ -252,7 +252,6 @@ function validateRule(page, $element, callback) {
252 regionCode; 252 regionCode;
253 253
254 254
255 -  
256 // 根据需求http://redmine.yoho.cn/issues/3117改成上边的正则 255 // 根据需求http://redmine.yoho.cn/issues/3117改成上边的正则
257 // 对应的错误提示语也改了,感觉要不了多久就会改回来 256 // 对应的错误提示语也改了,感觉要不了多久就会改回来
258 // pwdReg = /^([a-zA-Z0-9\-\+_!@\#$%\^&\*\(\)\:\;\.=\[\]\\\',\?]){6,20}$/gi; 257 // pwdReg = /^([a-zA-Z0-9\-\+_!@\#$%\^&\*\(\)\:\;\.=\[\]\\\',\?]){6,20}$/gi;
@@ -279,7 +278,7 @@ function validateRule(page, $element, callback) { @@ -279,7 +278,7 @@ function validateRule(page, $element, callback) {
279 } 278 }
280 } 279 }
281 280
282 - // 图形验证码校验 281 + // 图形验证码校验
283 } else if ($element.hasClass('captcha')) { 282 } else if ($element.hasClass('captcha')) {
284 if (val === '') { 283 if (val === '') {
285 validateResult[1].message = '请输入图形验证码'; 284 validateResult[1].message = '请输入图形验证码';
@@ -304,7 +303,7 @@ function validateRule(page, $element, callback) { @@ -304,7 +303,7 @@ function validateRule(page, $element, callback) {
304 303
305 } 304 }
306 305
307 - // 短信验证码校验 306 + // 短信验证码校验
308 } else if ($element.hasClass('msg-captcha')) { 307 } else if ($element.hasClass('msg-captcha')) {
309 308
310 if (val === '') { 309 if (val === '') {
@@ -329,7 +328,7 @@ function validateRule(page, $element, callback) { @@ -329,7 +328,7 @@ function validateRule(page, $element, callback) {
329 } 328 }
330 } 329 }
331 330
332 - // 密码校验 331 + // 密码校验
333 } else if ($element.hasClass('pwd')) { 332 } else if ($element.hasClass('pwd')) {
334 if (val === '') { 333 if (val === '') {
335 validateResult[3].message = '请输入密码'; 334 validateResult[3].message = '请输入密码';
@@ -349,7 +348,7 @@ function validateRule(page, $element, callback) { @@ -349,7 +348,7 @@ function validateRule(page, $element, callback) {
349 } 348 }
350 return callback(); 349 return callback();
351 350
352 - // 二次密码校验 351 + // 二次密码校验
353 } else if ($element.hasClass('repwd')) { 352 } else if ($element.hasClass('repwd')) {
354 if (val === '') { 353 if (val === '') {
355 validateResult[4].message = '请输入密码确认'; 354 validateResult[4].message = '请输入密码确认';
@@ -572,9 +571,9 @@ exports.init = function(page) { @@ -572,9 +571,9 @@ exports.init = function(page) {
572 }).blur(function() { 571 }).blur(function() {
573 572
574 /* validateRule($(this), function() { 573 /* validateRule($(this), function() {
575 - showErrTip();  
576 - showBorder(); // 显示红色边框  
577 - });*/ 574 + showErrTip();
  575 + showBorder(); // 显示红色边框
  576 + });*/
578 }); 577 });
579 578
580 $regionSelect.change(function() { 579 $regionSelect.change(function() {
@@ -721,4 +720,15 @@ exports.init = function(page) { @@ -721,4 +720,15 @@ exports.init = function(page) {
721 $countDown.text(countDown--); 720 $countDown.text(countDown--);
722 }, 1000); 721 }, 1000);
723 } 722 }
  723 +
  724 + // 设置 refer 信息
  725 + (function() {
  726 + var refer = queryString().refer || ''; // eslint-disable-line
  727 +
  728 + if (refer) {
  729 + setCookie('refer', refer); // eslint-disable-line
  730 + } else {
  731 + setCookie('refer', ''); // eslint-disable-line
  732 + }
  733 + }());
724 }; 734 };
  1 +/* eslint-disable */
  2 +/**
  3 + * Created by TaoHuang on 2016/10/10.
  4 + */
  5 +
  6 +var $ = require('yoho-jquery');
  7 +
  8 +require('./throttle');
  9 +
  10 +function dataLazyLoad(doc) {
  11 +
  12 + // 兼容低版本 IE
  13 + Function.prototype.bind = Function.prototype.bind || function(context) {
  14 + var that = this;
  15 + return function() {
  16 + return that.apply(context, arguments);
  17 + };
  18 + };
  19 +
  20 + // 工具方法 begin
  21 + var Util = {
  22 + getElements: function(cls) {
  23 + return $(cls).toArray();
  24 + },
  25 + addEvent: function(ele, type, fn) {
  26 + ele.attachEvent ? ele.attachEvent("on" + type, fn) : ele.addEventListener(type, fn, false);
  27 + },
  28 + removeEvent: function(ele, type, fn) {
  29 + ele.detachEvent ? ele.detachEvent("on" + type, fn) : ele.removeEventListener(type, fn, false);
  30 + },
  31 + getPos: function(ele) {
  32 + var pos = {
  33 + x: 0,
  34 + y: 0
  35 + };
  36 +
  37 + var offset = $(ele).offset();
  38 +
  39 + pos.x = offset.left;
  40 + pos.y = offset.top;
  41 +
  42 + return pos;
  43 + },
  44 + getViewport: function() {
  45 + var html = doc.documentElement;
  46 +
  47 + return {
  48 + w: !window.innerWidth ? html.clientHeight : window.innerWidth,
  49 + h: !window.innerHeight ? html.clientHeight : window.innerHeight
  50 + };
  51 + },
  52 + getScrollHeight: function() {
  53 + html = doc.documentElement, bd = doc.body;
  54 + return Math.max(window.pageYOffset || 0, html.scrollTop, bd.scrollTop);
  55 + },
  56 + getEleSize: function(ele) {
  57 + return {
  58 + w: ele.offsetWidth,
  59 + h: ele.offsetHeight
  60 + };
  61 + }
  62 + };
  63 + // 工具方法 end
  64 +
  65 + return {
  66 + threshold: 0, // {number} 阈值,预加载高度,单位(px)
  67 + els: null, // {Array} 延迟加载元素集合(数组)
  68 + fn: null, // {Function} scroll、resize、touchmove 所绑定方法,即为 pollTextareas()
  69 +
  70 + evalScripts: function(code) {
  71 + var head = $(".yoho-footer")[0],
  72 + js = doc.createElement("script");
  73 +
  74 + js.text = code;
  75 + head.appendChild(js, head.firstChild);
  76 + head.removeChild(js);
  77 + },
  78 +
  79 + evalStyles: function(code) {
  80 + var head = doc.getElementsByTagName("head")[0],
  81 + css = doc.createElement("style");
  82 +
  83 + css.type = "text/css";
  84 +
  85 + try {
  86 + css.appendChild(doc.createTextNode(code));
  87 + } catch (e) {
  88 + css.styleSheet.cssText = code;
  89 + }
  90 +
  91 + head.appendChild(css);
  92 + },
  93 +
  94 + extractCode: function(str, isStyle) {
  95 + var cata = isStyle ? "style" : "script",
  96 + scriptFragment = "<" + cata + "[^>]*>([\\S\\s]*?)</" + cata + "\\s*>",
  97 + matchAll = new RegExp(scriptFragment, "img"),
  98 + matchOne = new RegExp(scriptFragment, "im"),
  99 + matchResults = str.match(matchAll) || [],
  100 + ret = [];
  101 +
  102 + for (var i = 0, len = matchResults.length; i < len; i++) {
  103 + var temp = (matchResults[i].match(matchOne) || ["", ""])[1];
  104 + temp && ret.push(temp);
  105 + }
  106 + return ret;
  107 + },
  108 +
  109 + decodeHTML: function(str) {
  110 + return str.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")
  111 + .replace(/&lt;/g, '<').replace(/&gt;/g, '>');
  112 + },
  113 +
  114 + insert: function(ele) {
  115 + var parent = ele.parentNode,
  116 + txt = this.decodeHTML(ele.innerHTML),
  117 + matchStyles = this.extractCode(txt, true),
  118 + matchScripts = this.extractCode(txt);
  119 +
  120 + parent.innerHTML = txt
  121 + .replace(new RegExp("<script[^>]*>([\\S\\s]*?)</script\\s*>", "img"), "")
  122 + .replace(new RegExp("<style[^>]*>([\\S\\s]*?)</style\\s*>", "img"), "");
  123 +
  124 + if (matchStyles.length) {
  125 + for (var i = matchStyles.length; i--;) {
  126 + this.evalStyles(matchStyles[i]);
  127 + }
  128 + }
  129 +
  130 +
  131 + // 如果延迟部分需要做 loading 效果
  132 + parent.className = parent.className.replace("loading", "");
  133 +
  134 + if (matchScripts.length) {
  135 + for (var i = 0, len = matchScripts.length; i < len; i++) {
  136 + this.evalScripts(matchScripts[i]);
  137 + }
  138 + }
  139 + },
  140 +
  141 + inView: function(ele) {
  142 + var top = Util.getPos(ele).y
  143 + , viewVal = Util.getViewport().h
  144 + , scrollVal = Util.getScrollHeight()
  145 + , eleHeight = Util.getEleSize(ele).h;
  146 +
  147 + if (top >= scrollVal - eleHeight - this.threshold && top <= scrollVal + viewVal + this.threshold) {
  148 + return true;
  149 + }
  150 +
  151 + return false;
  152 + },
  153 +
  154 + pollTextareas: function() {
  155 + // 需延迟加载的元素已经全部加载完
  156 + if (!this.els.length) {
  157 + Util.removeEvent(window, "scroll", this.fn);
  158 + Util.removeEvent(window, "resize", this.fn);
  159 + Util.removeEvent(doc.body, "touchMove", this.fn);
  160 + return;
  161 + }
  162 +
  163 + // 判断是否需要加载
  164 + for (var i = this.els.length; i--;) {
  165 + var ele = this.els[i];
  166 +
  167 + if (!this.inView(ele)) {
  168 + continue;
  169 + }
  170 +
  171 + this.insert(ele);
  172 + this.els.splice(i, 1);
  173 + }
  174 + },
  175 +
  176 + init: function(config) {
  177 + var cls = config.cls;
  178 + this.threshold = config.threshold ? config.threshold : 0;
  179 +
  180 + this.els = Array.prototype.slice.call(Util.getElements(cls));
  181 + this.fn = this.pollTextareas.bind(this);
  182 +
  183 + this.fn();
  184 +
  185 + var _this = this;
  186 + Util.addEvent(window, "scroll", $.throttle(500, true, _this.fn));
  187 + Util.addEvent(window, "resize", $.throttle(500, true, _this.fn));
  188 + Util.addEvent(doc.body, "touchMove", $.throttle(500, true, _this.fn));
  189 + }
  190 + };
  191 +}
  192 +
  193 +module.exports = dataLazyLoad;
  194 +
  195 +/**
  196 + * demo
  197 + * datalazyload.init({
  198 + * cls: ".datalazyload", // 需要延迟加载的类,即 textarea 的类名
  199 + * threshold: 100 // 距离底部多高,进行延迟加载的阈值
  200 + * });
  201 + */
  1 +/* eslint-disable */
  2 +/*!
  3 + * jQuery throttle / debounce - v1.1 - 3/7/2010
  4 + * http://benalman.com/projects/jquery-throttle-debounce-plugin/
  5 + *
  6 + * Copyright (c) 2010 "Cowboy" Ben Alman
  7 + * Dual licensed under the MIT and GPL licenses.
  8 + * http://benalman.com/about/license/
  9 + */
  10 +
  11 +// Script: jQuery throttle / debounce: Sometimes, less is more!
  12 +//
  13 +// *Version: 1.1, Last updated: 3/7/2010*
  14 +//
  15 +// Project Home - http://benalman.com/projects/jquery-throttle-debounce-plugin/
  16 +// GitHub - http://github.com/cowboy/jquery-throttle-debounce/
  17 +// Source - http://github.com/cowboy/jquery-throttle-debounce/raw/master/jquery.ba-throttle-debounce.js
  18 +// (Minified) - http://github.com/cowboy/jquery-throttle-debounce/raw/master/jquery.ba-throttle-debounce.min.js (0.7kb)
  19 +//
  20 +// About: License
  21 +//
  22 +// Copyright (c) 2010 "Cowboy" Ben Alman,
  23 +// Dual licensed under the MIT and GPL licenses.
  24 +// http://benalman.com/about/license/
  25 +//
  26 +// About: Examples
  27 +//
  28 +// These working examples, complete with fully commented code, illustrate a few
  29 +// ways in which this plugin can be used.
  30 +//
  31 +// Throttle - http://benalman.com/code/projects/jquery-throttle-debounce/examples/throttle/
  32 +// Debounce - http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
  33 +//
  34 +// About: Support and Testing
  35 +//
  36 +// Information about what version or versions of jQuery this plugin has been
  37 +// tested with, what browsers it has been tested in, and where the unit tests
  38 +// reside (so you can test it yourself).
  39 +//
  40 +// jQuery Versions - none, 1.3.2, 1.4.2
  41 +// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome 4-5, Opera 9.6-10.1.
  42 +// Unit Tests - http://benalman.com/code/projects/jquery-throttle-debounce/unit/
  43 +//
  44 +// About: Release History
  45 +//
  46 +// 1.1 - (3/7/2010) Fixed a bug in <jQuery.throttle> where trailing callbacks
  47 +// executed later than they should. Reworked a fair amount of internal
  48 +// logic as well.
  49 +// 1.0 - (3/6/2010) Initial release as a stand-alone project. Migrated over
  50 +// from jquery-misc repo v0.4 to jquery-throttle repo v1.0, added the
  51 +// no_trailing throttle parameter and debounce functionality.
  52 +//
  53 +// Topic: Note for non-jQuery users
  54 +//
  55 +// jQuery isn't actually required for this plugin, because nothing internal
  56 +// uses any jQuery methods or properties. jQuery is just used as a namespace
  57 +// under which these methods can exist.
  58 +//
  59 +// Since jQuery isn't actually required for this plugin, if jQuery doesn't exist
  60 +// when this plugin is loaded, the method described below will be created in
  61 +// the `Cowboy` namespace. Usage will be exactly the same, but instead of
  62 +// $.method() or jQuery.method(), you'll need to use Cowboy.method().
  63 +
  64 +(function($) {
  65 + '$:nomunge'; // Used by YUI compressor.
  66 +
  67 + // Since jQuery really isn't required for this plugin, use `jQuery` as the
  68 + // namespace only if it already exists, otherwise use the `Cowboy` namespace,
  69 + // creating it if necessary.
  70 +
  71 + // Internal method reference.
  72 + var jq_throttle;
  73 +
  74 + // Method: jQuery.throttle
  75 + //
  76 + // Throttle execution of a function. Especially useful for rate limiting
  77 + // execution of handlers on events like resize and scroll. If you want to
  78 + // rate-limit execution of a function to a single time, see the
  79 + // <jQuery.debounce> method.
  80 + //
  81 + // In this visualization, | is a throttled-function call and X is the actual
  82 + // callback execution:
  83 + //
  84 + // > Throttled with `no_trailing` specified as false or unspecified:
  85 + // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
  86 + // > X X X X X X X X X X X X
  87 + // >
  88 + // > Throttled with `no_trailing` specified as true:
  89 + // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
  90 + // > X X X X X X X X X X
  91 + //
  92 + // Usage:
  93 + //
  94 + // > var throttled = jQuery.throttle( delay, [ no_trailing, ] callback );
  95 + // >
  96 + // > jQuery('selector').bind( 'someevent', throttled );
  97 + // > jQuery('selector').unbind( 'someevent', throttled );
  98 + //
  99 + // This also works in jQuery 1.4+:
  100 + //
  101 + // > jQuery('selector').bind( 'someevent', jQuery.throttle( delay, [ no_trailing, ] callback ) );
  102 + // > jQuery('selector').unbind( 'someevent', callback );
  103 + //
  104 + // Arguments:
  105 + //
  106 + // delay - (Number) A zero-or-greater delay in milliseconds. For event
  107 + // callbacks, values around 100 or 250 (or even higher) are most useful.
  108 + // no_trailing - (Boolean) Optional, defaults to false. If no_trailing is
  109 + // true, callback will only execute every `delay` milliseconds while the
  110 + // throttled-function is being called. If no_trailing is false or
  111 + // unspecified, callback will be executed one final time after the last
  112 + // throttled-function call. (After the throttled-function has not been
  113 + // called for `delay` milliseconds, the internal counter is reset)
  114 + // callback - (Function) A function to be executed after delay milliseconds.
  115 + // The `this` context and all arguments are passed through, as-is, to
  116 + // `callback` when the throttled-function is executed.
  117 + //
  118 + // Returns:
  119 + //
  120 + // (Function) A new, throttled, function.
  121 +
  122 + $.throttle = jq_throttle = function(delay, no_trailing, callback, debounce_mode) {
  123 + // After wrapper has stopped being called, this timeout ensures that
  124 + // `callback` is executed at the proper times in `throttle` and `end`
  125 + // debounce modes.
  126 + var timeout_id,
  127 +
  128 + // Keep track of the last time `callback` was executed.
  129 + last_exec = 0;
  130 +
  131 + // `no_trailing` defaults to falsy.
  132 + if (typeof no_trailing !== 'boolean') {
  133 + debounce_mode = callback;
  134 + callback = no_trailing;
  135 + no_trailing = undefined;
  136 + }
  137 +
  138 + // The `wrapper` function encapsulates all of the throttling / debouncing
  139 + // functionality and when executed will limit the rate at which `callback`
  140 + // is executed.
  141 + function wrapper() {
  142 + var that = this,
  143 + elapsed = +new Date() - last_exec,
  144 + args = arguments;
  145 +
  146 + // Execute `callback` and update the `last_exec` timestamp.
  147 + function exec() {
  148 + last_exec = +new Date();
  149 + callback.apply(that, args);
  150 + };
  151 +
  152 + // If `debounce_mode` is true (at_begin) this is used to clear the flag
  153 + // to allow future `callback` executions.
  154 + function clear() {
  155 + timeout_id = undefined;
  156 + };
  157 +
  158 + if (debounce_mode && !timeout_id) {
  159 + // Since `wrapper` is being called for the first time and
  160 + // `debounce_mode` is true (at_begin), execute `callback`.
  161 + exec();
  162 + }
  163 +
  164 + // Clear any existing timeout.
  165 + timeout_id && clearTimeout(timeout_id);
  166 +
  167 + if (debounce_mode === undefined && elapsed > delay) {
  168 + // In throttle mode, if `delay` time has been exceeded, execute
  169 + // `callback`.
  170 + exec();
  171 +
  172 + } else if (no_trailing !== true) {
  173 + // In trailing throttle mode, since `delay` time has not been
  174 + // exceeded, schedule `callback` to execute `delay` ms after most
  175 + // recent execution.
  176 + //
  177 + // If `debounce_mode` is true (at_begin), schedule `clear` to execute
  178 + // after `delay` ms.
  179 + //
  180 + // If `debounce_mode` is false (at end), schedule `callback` to
  181 + // execute after `delay` ms.
  182 + timeout_id = setTimeout(debounce_mode ? clear : exec, debounce_mode === undefined ? delay - elapsed : delay);
  183 + }
  184 + };
  185 +
  186 + // Set the guid of `wrapper` function to the same of original callback, so
  187 + // it can be removed in jQuery 1.4+ .unbind or .die by using the original
  188 + // callback as a reference.
  189 + if ($.guid) {
  190 + wrapper.guid = callback.guid = callback.guid || $.guid++;
  191 + }
  192 +
  193 + // Return the wrapper function.
  194 + return wrapper;
  195 + };
  196 +
  197 + // Method: jQuery.debounce
  198 + //
  199 + // Debounce execution of a function. Debouncing, unlike throttling,
  200 + // guarantees that a function is only executed a single time, either at the
  201 + // very beginning of a series of calls, or at the very end. If you want to
  202 + // simply rate-limit execution of a function, see the <jQuery.throttle>
  203 + // method.
  204 + //
  205 + // In this visualization, | is a debounced-function call and X is the actual
  206 + // callback execution:
  207 + //
  208 + // > Debounced with `at_begin` specified as false or unspecified:
  209 + // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
  210 + // > X X
  211 + // >
  212 + // > Debounced with `at_begin` specified as true:
  213 + // > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
  214 + // > X X
  215 + //
  216 + // Usage:
  217 + //
  218 + // > var debounced = jQuery.debounce( delay, [ at_begin, ] callback );
  219 + // >
  220 + // > jQuery('selector').bind( 'someevent', debounced );
  221 + // > jQuery('selector').unbind( 'someevent', debounced );
  222 + //
  223 + // This also works in jQuery 1.4+:
  224 + //
  225 + // > jQuery('selector').bind( 'someevent', jQuery.debounce( delay, [ at_begin, ] callback ) );
  226 + // > jQuery('selector').unbind( 'someevent', callback );
  227 + //
  228 + // Arguments:
  229 + //
  230 + // delay - (Number) A zero-or-greater delay in milliseconds. For event
  231 + // callbacks, values around 100 or 250 (or even higher) are most useful.
  232 + // at_begin - (Boolean) Optional, defaults to false. If at_begin is false or
  233 + // unspecified, callback will only be executed `delay` milliseconds after
  234 + // the last debounced-function call. If at_begin is true, callback will be
  235 + // executed only at the first debounced-function call. (After the
  236 + // throttled-function has not been called for `delay` milliseconds, the
  237 + // internal counter is reset)
  238 + // callback - (Function) A function to be executed after delay milliseconds.
  239 + // The `this` context and all arguments are passed through, as-is, to
  240 + // `callback` when the debounced-function is executed.
  241 + //
  242 + // Returns:
  243 + //
  244 + // (Function) A new, debounced, function.
  245 +
  246 + $.debounce = function(delay, at_begin, callback) {
  247 + return callback === undefined
  248 + ? jq_throttle(delay, at_begin, false)
  249 + : jq_throttle(delay, callback, at_begin !== false);
  250 + };
  251 +
  252 +})(jQuery);
@@ -11,10 +11,15 @@ @@ -11,10 +11,15 @@
11 11
12 var $ = require('yoho-jquery'), 12 var $ = require('yoho-jquery'),
13 lazyLoad = require('yoho-jquery-lazyload'), 13 lazyLoad = require('yoho-jquery-lazyload'),
  14 + dataLazyLoad = require('../plugins/lazy-load')(document),
14 Handlebars = require('yoho-handlebars'); 15 Handlebars = require('yoho-handlebars');
15 16
  17 +var bindEvent = $.Callbacks(); // eslint-disable-line
  18 +
16 var $main = $('.main'), 19 var $main = $('.main'),
17 - id = $main.data('id'); 20 + id = $main.data('id'),
  21 + md5 = $main.data('md5'),
  22 + skn = $main.data('skn');
18 23
19 var SLIDETIME = 200; 24 var SLIDETIME = 200;
20 25
@@ -24,475 +29,480 @@ var colTxt = { @@ -24,475 +29,480 @@ var colTxt = {
24 hover: '取消收藏' 29 hover: '取消收藏'
25 }; 30 };
26 31
27 -var $imgShow = $('#img-show'),  
28 - $thumbs = $('#thumbs > .thumb-wrap'); 32 +var $saleReturn = $('#saleReturn');
29 33
30 -var $size = $('#sizes'),  
31 - $sizes = $size.children('.size'),  
32 - $sizeWarn = $size.find('.size-warn'),  
33 - $colorSizeTip = $size.children('.color-size-tip'); 34 +bindEvent.add(function() {
  35 + var $imgShow = $('#img-show'),
  36 + $thumbs = $('#thumbs > .thumb-wrap');
34 37
35 -var $num = $('#num'),  
36 - $plusNum = $('#plus-num'),  
37 - $minusNum = $('#minus-num'); 38 + var $size = $('#sizes'),
  39 + $sizes = $size.children('.size'),
  40 + $sizeWarn = $size.find('.size-warn'),
  41 + $colorSizeTip = $size.children('.color-size-tip');
38 42
39 -var $addToCart = $('#add-to-cart'),  
40 - $soldOut = $('#sold-out'); 43 + var $num = $('#num'),
  44 + $plusNum = $('#plus-num'),
  45 + $minusNum = $('#minus-num');
41 46
42 -var $lcContainer = $('.lc-container'),  
43 - $itemBuy = $('.item-buy'); 47 + var $addToCart = $('#add-to-cart'),
  48 + $soldOut = $('#sold-out');
44 49
45 -var $descColor = $('#desc-color'); 50 + var $lcContainer = $('.lc-container'),
  51 + $itemBuy = $('.item-buy');
46 52
47 -var thumbsLoaded = {}; 53 + var $descColor = $('#desc-color');
48 54
49 -var isTicket = $('input[name="isTicket"]').length > 0 && $('input[name="isTicket"]').val() === 'true'; 55 + var thumbsLoaded = {};
50 56
51 -var maxStock = -1; // 记录当前选中的颜色-尺码的库存量,若为-1,代表未选择尺码 57 + var isTicket = $('input[name="isTicket"]').length > 0 && $('input[name="isTicket"]').val() === 'true';
52 58
53 -var Alert = require('../common/dialog').Alert; 59 + var maxStock = -1; // 记录当前选中的颜色-尺码的库存量,若为-1,代表未选择尺码
54 60
55 -function imgShow(src) {  
56 - $imgShow.attr('src', src);  
57 -} 61 + var Alert = require('../common/dialog').Alert;
58 62
59 -/**  
60 - * 获取当前选择的商品数目  
61 - * @return [Number]  
62 - */  
63 -function getNum() {  
64 - return +$num.text();  
65 -} 63 + function imgShow(src) {
  64 + $imgShow.attr('src', src);
  65 + }
66 66
67 -// 重置Num显示为1  
68 -function resetNum() {  
69 - $num.text('1'); 67 + /**
  68 + * 获取当前选择的商品数目
  69 + * @return [Number]
  70 + */
  71 + function getNum() {
  72 + return +$num.text();
  73 + }
70 74
71 - // +-按钮状态重置  
72 - if (maxStock === 1 || maxStock === 0) { 75 + // 重置Num显示为1
  76 + function resetNum() {
  77 + $num.text('1');
73 78
74 - // 数目为1/0时  
75 - $plusNum.addClass('dis');  
76 - $minusNum.addClass('dis');  
77 - } else {  
78 - $plusNum.removeClass('dis');  
79 - $minusNum.addClass('dis'); 79 + // +-按钮状态重置
  80 + if (maxStock === 1 || maxStock === 0) {
  81 +
  82 + // 数目为1/0时
  83 + $plusNum.addClass('dis');
  84 + $minusNum.addClass('dis');
  85 + } else {
  86 + $plusNum.removeClass('dis');
  87 + $minusNum.addClass('dis');
  88 + }
80 } 89 }
81 -}  
82 90
83 -// 加入购物车和已售罄状态控制  
84 -function switchBtnStatus($color) {  
85 - if ($itemBuy.length === 0) { 91 + // 加入购物车和已售罄状态控制
  92 + function switchBtnStatus($color) {
  93 + if ($itemBuy.length === 0) {
86 94
87 - // 全部售罄,按钮状态不变  
88 - return;  
89 - } 95 + // 全部售罄,按钮状态不变
  96 + return;
  97 + }
90 98
91 - // 选中的商品存量为0或者某个颜色的total为0  
92 - if (maxStock === 0 || ($color && ~~$color.data('total') === 0)) {  
93 - $itemBuy.addClass('hide'); // 隐藏加入购物车、即将开始、立即购买等相同位置的按钮  
94 - $soldOut.removeClass('hide');  
95 - } else { 99 + // 选中的商品存量为0或者某个颜色的total为0
  100 + if (maxStock === 0 || ($color && ~~$color.data('total') === 0)) {
  101 + $itemBuy.addClass('hide'); // 隐藏加入购物车、即将开始、立即购买等相同位置的按钮
  102 + $soldOut.removeClass('hide');
  103 + } else {
96 104
97 - // 包括默认的-1情况下  
98 - $itemBuy.removeClass('hide');  
99 - $soldOut.addClass('hide'); 105 + // 包括默认的-1情况下
  106 + $itemBuy.removeClass('hide');
  107 + $soldOut.addClass('hide');
  108 + }
100 } 109 }
101 -}  
102 110
103 -// size warn  
104 -function showSizeWarn() {  
105 - $sizes.not('.hide').children('.size-warn').removeClass('hide');  
106 -} 111 + // size warn
  112 + function showSizeWarn() {
  113 + $sizes.not('.hide').children('.size-warn').removeClass('hide');
  114 + }
107 115
108 -// size title  
109 -(function() {  
110 - var $sizeTitleJson = $('#size-title-json'),  
111 - jsonHtml = $sizeTitleJson.html(),  
112 - sizeTitle; 116 + // size title
  117 + (function() {
  118 + var $sizeTitleJson = $('#size-title-json'),
  119 + jsonHtml = $sizeTitleJson.html(),
  120 + sizeTitle;
113 121
114 - if (!jsonHtml) {  
115 - return;  
116 - } 122 + if (!jsonHtml) {
  123 + return;
  124 + }
117 125
118 - sizeTitle = $.parseJSON(jsonHtml); 126 + sizeTitle = $.parseJSON(jsonHtml);
119 127
120 - // 数据获取后删除  
121 - $sizeTitleJson.remove(); 128 + // 数据获取后删除
  129 + $sizeTitleJson.remove();
122 130
123 - $sizes.children('li').each(function() {  
124 - var $this = $(this),  
125 - key = $this.data('name'); 131 + $sizes.children('li').each(function() {
  132 + var $this = $(this),
  133 + key = $this.data('name');
126 134
127 - $this.data('title', sizeTitle[key]);  
128 - });  
129 -}()); 135 + $this.data('title', sizeTitle[key]);
  136 + });
  137 + }());
130 138
131 -// 初始化thumbsLoaded  
132 -thumbsLoaded[$('.colors .focus').index()] = true; 139 + require('../plugins/share');
133 140
134 -// 品牌收藏  
135 -$('#brand-favour').click(function() {  
136 - var $this = $(this); 141 + // 品牌收藏
  142 + $('#brand-favour').click(function() {
  143 + var $this = $(this);
137 144
138 - $.ajax({  
139 - type: 'POST',  
140 - url: '/product/index/favoriteBrand',  
141 - data: {  
142 - brandId: $this.data('id')  
143 - }  
144 - }).then(function(data) {  
145 - if (data.code === 200) {  
146 - $this.toggleClass('coled');  
147 - } else if (data.code === 403) {  
148 - location.href = data.data.url;  
149 - } 145 + $.ajax({
  146 + type: 'POST',
  147 + url: '/product/index/favoriteBrand',
  148 + data: {
  149 + brandId: $this.data('id')
  150 + }
  151 + }).then(function(data) {
  152 + if (data.code === 200) {
  153 + $this.toggleClass('coled');
  154 + } else if (data.code === 403) {
  155 + location.href = data.data.url;
  156 + }
  157 + });
150 }); 158 });
151 -});  
152 159
153 -// 颜色  
154 -$('.colors').on('click', 'li', function() {  
155 - var $this = $(this),  
156 - index = $this.index(); 160 + // 颜色
  161 + $('.colors').on('click', 'li', function() {
  162 + var $this = $(this),
  163 + index = $this.index();
157 164
158 - var $imgs; 165 + var $imgs;
159 166
160 - // 初始化color-size显示或隐藏  
161 - $colorSizeTip.addClass('hide'); 167 + // 初始化color-size显示或隐藏
  168 + $colorSizeTip.addClass('hide');
162 169
163 - // 隐藏尺码提示  
164 - $sizeWarn.addClass('hide'); 170 + // 隐藏尺码提示
  171 + $sizeWarn.addClass('hide');
165 172
166 - maxStock = -1; 173 + maxStock = -1;
167 174
168 - if ($this.hasClass('focus')) { 175 + if ($this.hasClass('focus')) {
169 176
170 - // 已获取焦点的颜色再次点击,清除尺码的选中  
171 - $sizes.eq(index).children('li').removeClass('focus');  
172 - } else { 177 + // 已获取焦点的颜色再次点击,清除尺码的选中
  178 + $sizes.eq(index).children('li').removeClass('focus');
  179 + } else {
173 180
174 - // 未获取焦点,选择尺码  
175 - $this.siblings('.focus').removeClass('focus');  
176 - $this.addClass('focus'); 181 + // 未获取焦点,选择尺码
  182 + $this.siblings('.focus').removeClass('focus');
  183 + $this.addClass('focus');
177 184
178 - // 切换图片显示  
179 - $thumbs.not('.hide').addClass('hide');  
180 - $imgs = $thumbs.eq(index).removeClass('hide').find('img'); 185 + // 切换图片显示
  186 + $thumbs.not('.hide').addClass('hide');
  187 + $imgs = $thumbs.eq(index).removeClass('hide').find('img');
181 188
182 - if (typeof thumbsLoaded[index] === 'undefined') { 189 + if (typeof thumbsLoaded[index] === 'undefined') {
183 190
184 - // trigger layLoad  
185 - lazyLoad($imgs, {  
186 - event: 'sporty'  
187 - }); 191 + // trigger layLoad
  192 + lazyLoad($imgs, {
  193 + event: 'sporty'
  194 + });
188 195
189 - $imgs.trigger('sporty'); 196 + $imgs.trigger('sporty');
190 197
191 - thumbsLoaded[index] = true;  
192 - } 198 + thumbsLoaded[index] = true;
  199 + }
193 200
194 - imgShow($imgs.first().data('shower')); 201 + imgShow($imgs.first().data('shower'));
195 202
196 - // 切换详细信息的颜色显示  
197 - $descColor.text($this.data('color')); 203 + // 切换详细信息的颜色显示
  204 + $descColor.text($this.data('color'));
198 205
199 - // 切换尺码显示  
200 - $sizes.not('.hide').addClass('hide').children('li').removeClass('focus');  
201 - $sizes.eq(index).removeClass('hide'); 206 + // 切换尺码显示
  207 + $sizes.not('.hide').addClass('hide').children('li').removeClass('focus');
  208 + $sizes.eq(index).removeClass('hide');
202 209
203 - // 是否展览票  
204 - if (isTicket) {  
205 - $sizes.eq(index).children('li').trigger('click');  
206 - $('.chose-size').addClass('hide'); 210 + // 是否展览票
  211 + if (isTicket) {
  212 + $sizes.eq(index).children('li').trigger('click');
  213 + $('.chose-size').addClass('hide');
  214 + }
207 } 215 }
208 - }  
209 -  
210 - resetNum();  
211 - switchBtnStatus($this);  
212 -});  
213 216
214 -// 缩略图鼠标移入显示  
215 -$('#thumbs').on('mouseenter', '.thumb', function() {  
216 - imgShow($(this).data('shower'));  
217 -}); 217 + resetNum();
  218 + switchBtnStatus($this);
  219 + });
218 220
219 -// 尺码  
220 -$size.on('click', 'li', function() {  
221 - var $this = $(this); 221 + // 缩略图鼠标移入显示
  222 + $('#thumbs').on('mouseenter', '.thumb', function() {
  223 + imgShow($(this).data('shower'));
  224 + });
222 225
223 - if ($this.hasClass('focus')) {  
224 - return;  
225 - } 226 + // 尺码
  227 + $size.on('click', 'li', function() {
  228 + var $this = $(this);
226 229
227 - maxStock = +$this.data('num'); 230 + if ($this.hasClass('focus')) {
  231 + return;
  232 + }
228 233
229 - $this.siblings('.focus').removeClass('focus');  
230 - $this.addClass('focus'); 234 + maxStock = +$this.data('num');
231 235
232 - $colorSizeTip.html($this.data('title')).removeClass('hide'); 236 + $this.siblings('.focus').removeClass('focus');
  237 + $this.addClass('focus');
233 238
234 - $sizeWarn.addClass('hide'); 239 + $colorSizeTip.html($this.data('title')).removeClass('hide');
235 240
236 - switchBtnStatus(); 241 + $sizeWarn.addClass('hide');
237 242
238 - resetNum();  
239 -}).on('click', '.size-ruler', function() { 243 + switchBtnStatus();
240 244
241 - // 尺码帮助 245 + resetNum();
  246 + }).on('click', '.size-ruler', function() {
242 247
243 - $('body,html').animate({  
244 - scrollTop: $('.size-info').offset().top  
245 - }, 300);  
246 -}); 248 + // 尺码帮助
247 249
248 -// 增加购买数量  
249 -$plusNum.click(function() {  
250 - var num = getNum(); 250 + $('body,html').animate({
  251 + scrollTop: $('.size-info').offset().top
  252 + }, 300);
  253 + });
251 254
252 - if ($(this).hasClass('dis')) {  
253 - return;  
254 - } 255 + // 增加购买数量
  256 + $plusNum.click(function() {
  257 + var num = getNum();
255 258
256 - if (maxStock === -1) {  
257 - showSizeWarn();// 显示选择尺码提示  
258 - return;  
259 - } 259 + if ($(this).hasClass('dis')) {
  260 + return;
  261 + }
260 262
261 - // 已售罄  
262 - if (maxStock === 0) {  
263 - return;  
264 - } 263 + if (maxStock === -1) {
  264 + showSizeWarn();// 显示选择尺码提示
  265 + return;
  266 + }
265 267
266 - if (num === maxStock - 1) { 268 + // 已售罄
  269 + if (maxStock === 0) {
  270 + return;
  271 + }
267 272
268 - // +按钮不可点  
269 - $(this).addClass('dis');  
270 - } 273 + if (num === maxStock - 1) {
271 274
272 - // 数目为1时点+则移除-不可点状态  
273 - if (num === 1) {  
274 - $minusNum.removeClass('dis');  
275 - } 275 + // +按钮不可点
  276 + $(this).addClass('dis');
  277 + }
276 278
277 - $num.text(num + 1 > maxStock ? maxStock : num + 1);  
278 -}).on('selectstart', function() {  
279 - return false;  
280 -}); 279 + // 数目为1时点+则移除-不可点状态
  280 + if (num === 1) {
  281 + $minusNum.removeClass('dis');
  282 + }
281 283
282 -// 减少购买数量  
283 -$minusNum.click(function() {  
284 - var num = getNum(); 284 + $num.text(num + 1 > maxStock ? maxStock : num + 1);
  285 + }).on('selectstart', function() {
  286 + return false;
  287 + });
285 288
286 - if ($(this).hasClass('dis')) {  
287 - return;  
288 - } 289 + // 减少购买数量
  290 + $minusNum.click(function() {
  291 + var num = getNum();
289 292
290 - if (num === 2) {  
291 - $(this).addClass('dis');  
292 - } 293 + if ($(this).hasClass('dis')) {
  294 + return;
  295 + }
293 296
294 - if (num === maxStock) { 297 + if (num === 2) {
  298 + $(this).addClass('dis');
  299 + }
295 300
296 - // 恢复+可点  
297 - $plusNum.removeClass('dis');  
298 - } 301 + if (num === maxStock) {
299 302
300 - $num.text(num - 1 < 0 ? 0 : num - 1);  
301 -}).on('selectstart', function() {  
302 - return false;  
303 -}); 303 + // 恢复+可点
  304 + $plusNum.removeClass('dis');
  305 + }
304 306
305 -// 限购码  
306 -$('.get-lc:not(.dis)').hover(function() {  
307 - $lcContainer.removeClass('hide');  
308 -}, function() {  
309 - $lcContainer.addClass('hide');  
310 -}); 307 + $num.text(num - 1 < 0 ? 0 : num - 1);
  308 + }).on('selectstart', function() {
  309 + return false;
  310 + });
311 311
312 -// 加入购物车  
313 -$addToCart.click(function() {  
314 - if (maxStock === -1) {  
315 - showSizeWarn();  
316 - return;  
317 - } 312 + // 限购码
  313 + $('.get-lc:not(.dis)').hover(function() {
  314 + $lcContainer.removeClass('hide');
  315 + }, function() {
  316 + $lcContainer.addClass('hide');
  317 + });
318 318
319 - $.ajax({  
320 - type: 'POST',  
321 - url: '/cart/index/add',  
322 - data: {  
323 - productSku: $('.size:not(.hide) li.focus').data('sku'),  
324 - buyNumber: getNum() 319 + // 加入购物车
  320 + $addToCart.click(function() {
  321 + if (maxStock === -1) {
  322 + showSizeWarn();
  323 + return;
325 } 324 }
326 - }).then(function(data) {  
327 - var code = data.code;  
328 325
329 - if (code === 200) {  
330 - $('#type-chose').slideUp(SLIDETIME);  
331 - $('#balance').slideDown(SLIDETIME); 326 + $.ajax({
  327 + type: 'POST',
  328 + url: '/cart/index/add',
  329 + data: {
  330 + productSku: $('.size:not(.hide) li.focus').data('sku'),
  331 + buyNumber: getNum()
  332 + }
  333 + }).then(function(data) {
  334 + var code = data.code;
  335 +
  336 + if (code === 200) {
  337 + $('#type-chose').slideUp(SLIDETIME);
  338 + $('#balance').slideDown(SLIDETIME);
332 339
333 - $('#cart-num').text(data.data.goods_count); // 更新数目  
334 - } 340 + $('#cart-num').text(data.data.goods_count); // 更新数目
  341 + }
  342 + });
335 }); 343 });
336 -});  
337 -  
338 -// 收藏  
339 -$('#collect-product').click(function() {  
340 - var $this = $(this),  
341 - cancel;  
342 344
343 - if ($this.hasClass('coled')) {  
344 - cancel = true;  
345 - } 345 + // 收藏
  346 + $('#collect-product').click(function() {
  347 + var $this = $(this),
  348 + cancel;
346 349
347 - $.ajax({  
348 - type: 'POST',  
349 - url: '/product/item/togglecollect',  
350 - data: {  
351 - productId: id,  
352 - type: cancel ? 'cancel' : 'add' 350 + if ($this.hasClass('coled')) {
  351 + cancel = true;
353 } 352 }
354 - }).then(function(data) {  
355 - var code = data.code;  
356 353
357 - if (code === 200) { 354 + $.ajax({
  355 + type: 'POST',
  356 + url: '/product/item/togglecollect',
  357 + data: {
  358 + productId: id,
  359 + type: cancel ? 'cancel' : 'add'
  360 + }
  361 + }).then(function(data) {
  362 + var code = data.code;
  363 +
  364 + if (code === 200) {
358 365
359 - $this.toggleClass('coled'); 366 + $this.toggleClass('coled');
360 367
361 - if (cancel) {  
362 - $this.find('em').text(colTxt.def);  
363 - } else {  
364 - $this.find('em').text(colTxt.coled); 368 + if (cancel) {
  369 + $this.find('em').text(colTxt.def);
  370 + } else {
  371 + $this.find('em').text(colTxt.coled);
  372 + }
  373 + } else if (code === 403) {
  374 + location.href = data.data.url;
365 } 375 }
366 - } else if (code === 403) {  
367 - location.href = data.data.url; 376 + });
  377 + }).hover(function() {
  378 + var $this = $(this);
  379 +
  380 + if ($this.hasClass('coled')) {
  381 + $this.find('em').text(colTxt.hover);
368 } 382 }
369 - });  
370 -}).hover(function() {  
371 - var $this = $(this); 383 + }, function() {
  384 + var $this = $(this);
372 385
373 - if ($this.hasClass('coled')) {  
374 - $this.find('em').text(colTxt.hover);  
375 - }  
376 -}, function() {  
377 - var $this = $(this); 386 + if ($this.hasClass('coled')) {
  387 + $this.find('em').text(colTxt.coled);
  388 + }
  389 + });
378 390
379 - if ($this.hasClass('coled')) {  
380 - $this.find('em').text(colTxt.coled);  
381 - }  
382 -}); 391 + // 继续购物
  392 + $('#keep-shopping').click(function() {
  393 + $('#type-chose').slideDown(SLIDETIME);
  394 + $('#balance').slideUp(SLIDETIME);
  395 + });
383 396
384 -// 继续购物  
385 -$('#keep-shopping').click(function() {  
386 - $('#type-chose').slideDown(SLIDETIME);  
387 - $('#balance').slideUp(SLIDETIME);  
388 -}); 397 + // 电子票立即购买
  398 + $('#buy-ticket').click(function() {
  399 + var sku,
  400 + buyNumber;
389 401
390 -// 电子票立即购买  
391 -$('#buy-ticket').click(function() {  
392 - var sku,  
393 - buyNumber; 402 + if (maxStock === -1) {
  403 + showSizeWarn();
  404 + return;
  405 + }
394 406
395 - if (maxStock === -1) {  
396 - showSizeWarn();  
397 - return;  
398 - } 407 + if ($(this).hasClass('dis')) {
  408 + return;
  409 + }
399 410
400 - if ($(this).hasClass('dis')) {  
401 - return;  
402 - } 411 + sku = $('.size:not(.hide) li.focus').data('sku');
  412 + buyNumber = getNum();
403 413
404 - sku = $('.size:not(.hide) li.focus').data('sku');  
405 - buyNumber = getNum(); 414 + $.ajax({
  415 + type: 'POST',
  416 + url: '/cart/index/setTicket',
  417 + data: {
  418 + productSku: sku,
  419 + buyNumber: buyNumber
  420 + }
  421 + }).then(function(data) {
  422 + var $ticketForm,
  423 + myAlert;
  424 +
  425 + if (data.code * 1 === 200) {
  426 + $ticketForm = $('form[name="ticket-form"]');
  427 + $ticketForm.html(
  428 + '<input name="productSku" value="' + sku + '" />' +
  429 + '<input name="buyNumber" value="' + buyNumber + '" />'
  430 + );
  431 + $ticketForm.submit();
  432 + } else if (data.code * 1 === 401) {
  433 + document.location.href = data.message;
  434 + } else {
  435 + myAlert = new Alert(data.message);
  436 + myAlert.show();
  437 + }
  438 + });
  439 + });
406 440
407 - $.ajax({  
408 - type: 'POST',  
409 - url: '/cart/index/setTicket',  
410 - data: {  
411 - productSku: sku,  
412 - buyNumber: buyNumber  
413 - }  
414 - }).then(function(data) {  
415 - var $ticketForm, 441 + // 立即购买
  442 + $('#buy-now').click(function() {
  443 + var sku,
  444 + base,
416 myAlert; 445 myAlert;
417 446
418 - if (data.code * 1 === 200) {  
419 - $ticketForm = $('form[name="ticket-form"]');  
420 - $ticketForm.html(  
421 - '<input name="productSku" value="' + sku + '" />' +  
422 - '<input name="buyNumber" value="' + buyNumber + '" />'  
423 - );  
424 - $ticketForm.submit();  
425 - } else if (data.code * 1 === 401) {  
426 - document.location.href = data.message;  
427 - } else {  
428 - myAlert = new Alert(data.message);  
429 - myAlert.show(); 447 + if ($(this).hasClass('dis')) {
  448 + return;
430 } 449 }
431 - });  
432 -});  
433 -  
434 -// 立即购买  
435 -$('#buy-now').click(function() {  
436 - var sku,  
437 - base,  
438 - myAlert;  
439 450
440 - if (maxStock === -1) {  
441 - showSizeWarn();  
442 - return;  
443 - } 451 + if (maxStock === -1) {
  452 + showSizeWarn();
  453 + return;
  454 + }
444 455
445 - if ($(this).hasClass('dis')) {  
446 - return;  
447 - } 456 + base = $(this).data('base');
448 457
449 - base = $(this).data('base'); 458 + // 潮流尖货
  459 + if (!base) {
  460 + myAlert = new Alert('打开APP可抢购该商品哦~~');
  461 + myAlert.show();
  462 + return;
  463 + }
450 464
451 - // 潮流尖货  
452 - if (!base) {  
453 - myAlert = new Alert('打开APP可抢购该商品哦~~');  
454 - myAlert.show();  
455 - return;  
456 - } 465 + sku = $sizes.find('.focus').data('sku');
457 466
458 - sku = $sizes.find('.focus').data('sku'); 467 + if (sku && base) {
  468 + location.href = base + '?product_sku=' + sku;
  469 + }
  470 + });
459 471
460 - if (sku && base) {  
461 - location.href = base + '?product_sku=' + sku;  
462 - }  
463 -}); 472 + // 按钮鼠标移入效果【dis状态的按钮状态不变】
  473 + $('.buy-btn').mouseover(function() {
  474 + if ($(this).hasClass('dis')) {
  475 + return;
  476 + }
464 477
465 -// 按钮鼠标移入效果【dis状态的按钮状态不变】  
466 -$('.buy-btn').mouseover(function() {  
467 - if ($(this).hasClass('dis')) {  
468 - return;  
469 - } 478 + $(this).addClass('hover');
  479 + }).mouseleave(function() {
  480 + $(this).removeClass('hover');
  481 + });
470 482
471 - $(this).addClass('hover');  
472 -}).mouseleave(function() {  
473 - $(this).removeClass('hover');  
474 -}); 483 + // 只有一个尺码(多个或一个颜色)时默认选中第一个未售罄的颜色的第一个尺码
  484 + (function() {
  485 + var hasOnlyOneSize = true,
  486 + i;
475 487
476 -// 只有一个尺码(多个或一个颜色)时默认选中第一个未售罄的颜色的第一个尺码  
477 -(function() {  
478 - var hasOnlyOneSize = true,  
479 - i; 488 + for (i = 0; i < $sizes.length; i++) {
  489 + if ($sizes.eq(i).find('li').length !== 1) {
  490 + hasOnlyOneSize = false;
  491 + break;
  492 + }
  493 + }
480 494
481 - for (i = 0; i < $sizes.length; i++) {  
482 - if ($sizes.eq(i).find('li').length !== 1) {  
483 - hasOnlyOneSize = false;  
484 - break; 495 + if (hasOnlyOneSize) {
  496 + $sizes.eq($('.colors .focus').index()).find('li').click();
485 } 497 }
486 - } 498 + }());
487 499
488 - if (hasOnlyOneSize) {  
489 - $sizes.eq($('.colors .focus').index()).find('li').click();  
490 - }  
491 -}()); 500 +});
  501 +
  502 +// yas
  503 +require('../common');
492 504
493 -require('../common'); // yas  
494 require('./detail/latest-walk'); // 最近浏览 505 require('./detail/latest-walk'); // 最近浏览
495 -require('../plugins/share'); // 分享  
496 506
497 // 商品详情/材质洗涤切换 507 // 商品详情/材质洗涤切换
498 $('.description-material').on('click', '.title', function() { 508 $('.description-material').on('click', '.title', function() {
@@ -541,52 +551,40 @@ $('.after-service-switch').click(function() { @@ -541,52 +551,40 @@ $('.after-service-switch').click(function() {
541 } 551 }
542 }); 552 });
543 553
544 -  
545 // 商品详情区的热点 554 // 商品详情区的热点
546 -$.ajax({  
547 - type: 'GET',  
548 - url: '/product/detail/hotarea',  
549 - data: {  
550 - productId: id  
551 - }  
552 -}).then(function(html) {  
553 - $('#details-html').prepend(html);  
554 555
555 - // 修正热区尺寸使居中  
556 - $('.hot-point-wrap > img').load(function() {  
557 - $(this).parent('.hot-point-wrap').width($(this).width());  
558 - }); 556 +function fetchHotArea() {
  557 + $.ajax({
  558 + type: 'GET',
  559 + url: '/product/detail/hotarea',
  560 + data: {
  561 + productId: id
  562 + }
  563 + }).then(function(html) {
  564 + $('#details-html').prepend(html);
559 565
560 - // Bind Hover event  
561 - $('.hot-point').hover(function() {  
562 - $(this).addClass('hover');  
563 - }, function() {  
564 - $(this).removeClass('hover');  
565 - });  
566 -}); 566 + // 修正热区尺寸使居中
  567 + $('.hot-point-wrap > img').load(function() {
  568 + $(this).parent('.hot-point-wrap').width($(this).width());
  569 + });
567 570
568 -// 商品详情懒加载  
569 -lazyLoad($('#details-html img')); 571 + // Bind Hover event
  572 + $('.hot-point').hover(function() {
  573 + $(this).addClass('hover');
  574 + }, function() {
  575 + $(this).removeClass('hover');
  576 + });
  577 + });
  578 +}
570 579
571 -// 只有一个尺码(多个或一个颜色)时默认选中第一个未售罄的颜色的第一个尺码  
572 -(function() {  
573 - var hasOnlyOneSize = true,  
574 - i; 580 +window.fetchHotArea = fetchHotArea;
575 581
576 - for (i = 0; i < $sizes.length; i++) {  
577 - if ($sizes.eq(i).find('li').length !== 1) {  
578 - hasOnlyOneSize = false;  
579 - break;  
580 - }  
581 - } 582 +// 商品详情图片懒加载
  583 +lazyLoad($('#details-html img'));
582 584
583 - if (hasOnlyOneSize) {  
584 - $sizes.eq($('.colors .focus').index()).find('li').click();  
585 - }  
586 -}());  
587 585
588 // 咨询和评价 586 // 咨询和评价
589 -(function() { 587 +function fetchComment() {
590 var commentPage = 1, 588 var commentPage = 1,
591 consultPage = 1; 589 consultPage = 1;
592 590
@@ -773,4 +771,58 @@ lazyLoad($('#details-html img')); @@ -773,4 +771,58 @@ lazyLoad($('#details-html img'));
773 771
774 loadComments(); 772 loadComments();
775 loadConsults(); 773 loadConsults();
  774 +}
  775 +
  776 +window.fetchComment = fetchComment;
  777 +
  778 +// 特殊商品退换货
  779 +function fetchReturn() {
  780 + return $.ajax({
  781 + type: 'GET',
  782 + url: '/product/detail/return',
  783 + data: {
  784 + skn: skn
  785 + }
  786 + }).then(function(result) {
  787 + if (result.code === 200) {
  788 + if (result.data.result === 'N') {
  789 + $saleReturn.removeClass().addClass('not-support-saleReturned-service');
  790 + }
  791 + }
  792 + });
  793 +}
  794 +
  795 +window.fetchReturn = fetchReturn;
  796 +
  797 +// 首屏加载后,对比数据是否变化,如果变化,重新渲染,重新绑定事件
  798 +// 当前只渲染商品详情页的变化敏感的部分
  799 +(function() {
  800 + $.ajax({
  801 + type: 'GET',
  802 + url: '/product/detail/header',
  803 + dataType: 'html',
  804 + data: {
  805 + productId: id,
  806 + md5: md5
  807 + }
  808 + }).then(function(result) {
  809 + $('.main').html(result);
  810 + bindEvent.fire();
  811 + });
  812 +
  813 + $.ajax({
  814 + type: 'GET',
  815 + url: '/product/index/isfav',
  816 + data: {
  817 + brandId: $('#brand-favour').data('id')
  818 + }
  819 + }).then(function(result) {
  820 + if (result.code === 200) {
  821 + $('#brand-favour').addClass('coled');
  822 + }
  823 + });
  824 +
776 }()); 825 }());
  826 +
  827 +// 数据懒加载
  828 +dataLazyLoad.init({cls: '.datalazyload', threshold: 0});
@@ -7,10 +7,11 @@ @@ -7,10 +7,11 @@
7 var $ = require('yoho-jquery'); 7 var $ = require('yoho-jquery');
8 var Handlebars = require('yoho-handlebars'); 8 var Handlebars = require('yoho-handlebars');
9 var lazyLoad = require('yoho-jquery-lazyload'); 9 var lazyLoad = require('yoho-jquery-lazyload');
  10 +var dataLazyLoad = require('../../plugins/lazy-load')(document);
10 11
11 var $latestWalkCount = $('#latest-walk-count'); 12 var $latestWalkCount = $('#latest-walk-count');
12 13
13 -(function() { 14 +function fetchLatestWalk() {
14 var tpl; 15 var tpl;
15 16
16 if ($latestWalkCount.length === 0) { 17 if ($latestWalkCount.length === 0) {
@@ -59,4 +60,9 @@ var $latestWalkCount = $('#latest-walk-count'); @@ -59,4 +60,9 @@ var $latestWalkCount = $('#latest-walk-count');
59 } 60 }
60 } 61 }
61 }); 62 });
62 -}()); 63 +}
  64 +
  65 +window.fetchLatestWalk = fetchLatestWalk;
  66 +
  67 +// 数据懒加载
  68 +dataLazyLoad.init({cls: '.latest-walk-datalazyload', threshold: 700});
@@ -659,6 +659,29 @@ $(function() { @@ -659,6 +659,29 @@ $(function() {
659 } 659 }
660 }, 100); 660 }, 100);
661 661
  662 +
  663 + var rightsFirst = '<div class="item-content hide">' +
  664 + '<p class="item-title">新品立享9折</p>' +
  665 + '<p>学生购买原价新品时,可立即享受9折优惠,与VIP折扣不可同时享受。</p></div>';
  666 +
  667 + var rightsSecond = '<div class="item-content hide">' +
  668 + '<p class="item-title">每1元返1个有货币</p>' +
  669 + '<p>学生购买指定商品时,以商品的实际成交金额计算,每1元返1个有货币;</p>' +
  670 + '<p>返有货币时间:确认收货7日后,系统将自动将对应数量的有货币返至购买账户;</p>' +
  671 + '<p>有货币有效期:获得当日至次年12月31日,逾期自动作废;</p>' +
  672 + '<p>查看有货币:登录后,点击“个人中心”在“我的有货币”中可以查看有货币余额及明细。</p></div>';
  673 +
  674 + var rightsThird = '<div class="item-content hide">' +
  675 + '<p class="item-title">免单抽奖</p>' +
  676 + '<p>每月将在累计购物金额最高的学校中,抽取3名幸运学生用户,获得免单资格;</p>' +
  677 + '<p>免单用户名单将在每月第1个工作日公布在有货微信公众号上,可关注【有货YOHOBUY】;</p>' +
  678 + '<p>免单用户将在中奖次日获得与实付金额等额的现金券,使用时间:中奖当月。</p></div>';
  679 +
  680 + var rightsFourth = '<div class="item-content hide">' +
  681 + '<p class="item-title">学生专享活动</p>' +
  682 + '<p>每月不定期的开展学生专享活动,可打开有货APP推送,及时查收学生专享活动通知。</p></div>';
  683 +
  684 +
662 $('.coupon > a').each(function() { 685 $('.coupon > a').each(function() {
663 couponIds.push($(this).attr('data-id')); 686 couponIds.push($(this).attr('data-id'));
664 }); 687 });
@@ -790,6 +813,30 @@ $(function() { @@ -790,6 +813,30 @@ $(function() {
790 F_INDEX: 6, 813 F_INDEX: 6,
791 I_INDEX: parseInt(index, 0) + 1}); 814 I_INDEX: parseInt(index, 0) + 1});
792 }); 815 });
  816 +
  817 + // 学生权益,悬浮显示内容
  818 + $('#stuRights .stu-rights-item:eq(0)').append(rightsFirst);
  819 + $('#stuRights .stu-rights-item:eq(1)').append(rightsSecond);
  820 + $('#stuRights .stu-rights-item:eq(2)').append(rightsThird);
  821 + $('#stuRights .stu-rights-item:eq(3)').append(rightsFourth);
  822 +
  823 + // 悬浮效果
  824 + $('#stuRights .stu-rights-item:eq(0), #stuRights .stu-rights-item:eq(1),' +
  825 + '#stuRights .stu-rights-item:eq(2), #stuRights .stu-rights-item:eq(3)').hover(function() {
  826 + var content = $(this).find('.item-content');
  827 +
  828 + content.removeClass('hide');
  829 +
  830 + let gapHeight = parseInt(content.height(), 10) + 40 - parseInt($(this).height(), 10);
  831 +
  832 + if (gapHeight > 0) {
  833 + content.css('margin-top', -(gapHeight) + 'px');
  834 + }
  835 + }, function() {
  836 + var content = $(this).find('.item-content');
  837 +
  838 + content.addClass('hide').css('margin-top', '0');
  839 + });
793 }); 840 });
794 841
795 window.onbeforeunload = function() { 842 window.onbeforeunload = function() {
@@ -2,6 +2,8 @@ var $ = require('yoho-jquery'); @@ -2,6 +2,8 @@ var $ = require('yoho-jquery');
2 2
3 var $apiDom = $('#api-domain'); 3 var $apiDom = $('#api-domain');
4 4
  5 +require('./common');
  6 +
5 // var apiDomain = $apiDom.val(); // 获取数据的地址 7 // var apiDomain = $apiDom.val(); // 获取数据的地址
6 8
7 $apiDom.remove(); // 删除API信息 9 $apiDom.remove(); // 删除API信息
@@ -70,12 +72,12 @@ function setLoginStatus(data) { @@ -70,12 +72,12 @@ function setLoginStatus(data) {
70 if (data.result !== -1) { 72 if (data.result !== -1) {
71 name = formatUsernName(info.profileName); 73 name = formatUsernName(info.profileName);
72 loginHtml = '<span>Hi~</span>' + 74 loginHtml = '<span>Hi~</span>' +
73 - '<a href="//www.yohobuy.com/home?t=' + info.random + '">' + name + '</a>&nbsp;' +  
74 - '<a href="//www.yohobuy.com/logout.html">[退出]</a>'; 75 + '<a href="//www.yohobuy.com/home?t=' + info.random + '">' + name + '</a>&nbsp;' +
  76 + '<a href="//www.yohobuy.com/logout.html">[退出]</a>';
75 } else { 77 } else {
76 loginHtml = '<span>Hi~</span>&nbsp;' + 78 loginHtml = '<span>Hi~</span>&nbsp;' +
77 - '<a href="//www.yohobuy.com/signin.html">[请登录]</a>&nbsp;' +  
78 - '<a href="//www.yohobuy.com/reg.html">[免费注册]</a>'; 79 + '<a href="//www.yohobuy.com/signin.html?refer="' + window.location.href + '>[请登录]</a>&nbsp;' +
  80 + '<a href="//www.yohobuy.com/reg.html"' + window.location.href + '>[免费注册]</a>';
79 } 81 }
80 $('.header-tool li').eq(0).html(loginHtml); 82 $('.header-tool li').eq(0).html(loginHtml);
81 } 83 }
@@ -96,5 +98,22 @@ function actionLoginInfo() { @@ -96,5 +98,22 @@ function actionLoginInfo() {
96 }); 98 });
97 } 99 }
98 100
99 -actionLoginInfo(); // 获取登录信息 101 +// 获取头部登陆信息
  102 +(function() {
  103 + var uid = getUid(), //eslint-disable-line
  104 + profileName = getProfileName(); // eslint-disable-line
  105 +
  106 + var info = {
  107 + random: $.now(),
  108 + profileName: profileName
  109 + };
  110 +
  111 + if (uid !== 0) {
  112 + info.result = 1;
  113 + } else {
  114 + info.result = -1;
  115 + }
  116 +
  117 + setLoginStatus(info);
  118 +}());
100 119
@@ -324,7 +324,7 @@ @@ -324,7 +324,7 @@
324 margin-top: -28px; 324 margin-top: -28px;
325 width: 60px; 325 width: 60px;
326 height: 56px; 326 height: 56px;
327 - background-image: resolve(img/index/play.png); 327 + background-image: resolve(index/play.png);
328 } 328 }
329 329
330 .video-title { 330 .video-title {
@@ -714,7 +714,7 @@ @@ -714,7 +714,7 @@
714 margin-top: -28px; 714 margin-top: -28px;
715 width: 60px; 715 width: 60px;
716 height: 56px; 716 height: 56px;
717 - background-image: resolve(img/index/play.png); 717 + background-image: resolve(index/play.png);
718 } 718 }
719 719
720 .video-title { 720 .video-title {
@@ -812,7 +812,7 @@ @@ -812,7 +812,7 @@
812 left: 15px; 812 left: 15px;
813 width: 325px; 813 width: 325px;
814 height: 287px; 814 height: 287px;
815 - background: resolve(img/index/brands-layer.png) no-repeat; 815 + background: resolve(index/brands-layer.png) no-repeat;
816 font-weight: normal; 816 font-weight: normal;
817 817
818 .layer-content { 818 .layer-content {
@@ -864,7 +864,7 @@ @@ -864,7 +864,7 @@
864 } 864 }
865 865
866 .brands-layer-right { 866 .brands-layer-right {
867 - background: resolve(img/index/brands-layer.png) no-repeat 0 -287px; 867 + background: resolve(index/brands-layer.png) no-repeat 0 -287px;
868 left: -325px; 868 left: -325px;
869 top: -83px; 869 top: -83px;
870 870
@@ -136,6 +136,10 @@ @@ -136,6 +136,10 @@
136 margin-top: -19px; 136 margin-top: -19px;
137 } 137 }
138 138
  139 + > .hidden {
  140 + display: none;
  141 + }
  142 +
139 p { 143 p {
140 margin-bottom: 5px; 144 margin-bottom: 5px;
141 } 145 }
@@ -90,10 +90,10 @@ @@ -90,10 +90,10 @@
90 90
91 a { 91 a {
92 color: #8e8e8e; 92 color: #8e8e8e;
93 -  
94 - span {  
95 - color: #d0021b;  
96 - } 93 + }
  94 +
  95 + a > span {
  96 + color: #d0021b;
97 } 97 }
98 } 98 }
99 99
@@ -214,7 +214,7 @@ @@ -214,7 +214,7 @@
214 } 214 }
215 } 215 }
216 216
217 - .myyoho:hover { 217 + .myyoho-hover {
218 background-color: #dcdcdc; 218 background-color: #dcdcdc;
219 219
220 .simple-user-center { 220 .simple-user-center {
@@ -598,10 +598,15 @@ @@ -598,10 +598,15 @@
598 .nav-wrapper { 598 .nav-wrapper {
599 height: 40px; 599 height: 40px;
600 width: 100%; 600 width: 100%;
601 - background-color: #3a3a3a;  
602 position: relative; 601 position: relative;
603 602
604 .sub-nav-list { 603 .sub-nav-list {
  604 + display: none;
  605 +
  606 + &.cure {
  607 + display: block;
  608 + }
  609 +
605 li { 610 li {
606 line-height: 40px; 611 line-height: 40px;
607 padding-right: 46px; 612 padding-right: 46px;
@@ -720,6 +725,12 @@ @@ -720,6 +725,12 @@
720 } 725 }
721 } 726 }
722 727
  728 +.yoho-header.boys {
  729 + .nav-wrapper {
  730 + background-color: #3a3a3a;
  731 + }
  732 +}
  733 +
723 .yoho-header.girls { 734 .yoho-header.girls {
724 .search-2016 { 735 .search-2016 {
725 background-color: #ff88ae; 736 background-color: #ff88ae;
@@ -705,7 +705,7 @@ @@ -705,7 +705,7 @@
705 left: 15px; 705 left: 15px;
706 width: 325px; 706 width: 325px;
707 height: 287px; 707 height: 287px;
708 - background: resolve(/index/brands-layer.png) no-repeat; 708 + background: resolve(index/brands-layer.png) no-repeat;
709 font-weight: normal; 709 font-weight: normal;
710 710
711 .layer-content { 711 .layer-content {
@@ -750,11 +750,11 @@ @@ -750,11 +750,11 @@
750 } 750 }
751 } 751 }
752 .brands-layer-right { 752 .brands-layer-right {
753 - background: resolve(/index/brands-layer.png) no-repeat 0 -287px; 753 + background: resolve(index/brands-layer.png) no-repeat 0 -287px;
754 left: -325px; 754 left: -325px;
755 top: -83px; 755 top: -83px;
756 756
757 .layer-content { 757 .layer-content {
758 padding: 20px 35px 20px 20px; 758 padding: 20px 35px 20px 20px;
759 } 759 }
760 -}  
  760 +}
@@ -96,6 +96,7 @@ @@ -96,6 +96,7 @@
96 padding: 20px 0 20px 20px; 96 padding: 20px 0 20px 20px;
97 97
98 .stu-rights-item { 98 .stu-rights-item {
  99 + position: relative;
99 float: left; 100 float: left;
100 width: 262px; 101 width: 262px;
101 height: 200px; 102 height: 200px;
@@ -103,6 +104,7 @@ @@ -103,6 +104,7 @@
103 text-align: center; 104 text-align: center;
104 background-color: #fff; 105 background-color: #fff;
105 border-radius: 5px; 106 border-radius: 5px;
  107 + overflow: hidden;
106 } 108 }
107 109
108 .stu-rights-item img { 110 .stu-rights-item img {
@@ -110,6 +112,27 @@ @@ -110,6 +112,27 @@
110 height: 162px; 112 height: 162px;
111 margin: 20px 0; 113 margin: 20px 0;
112 } 114 }
  115 +
  116 + .stu-rights-item .item-content {
  117 + position: absolute;
  118 + top: 0;
  119 + padding: 20px;
  120 + margin-top: 0;
  121 + transition: margin-top 2s linear;
  122 + background: rgba(255,255,255,.95);
  123 + }
  124 +
  125 + .stu-rights-item .item-content .item-title {
  126 + font-weight: bold;
  127 + margin-bottom: 20px;
  128 + margin-top: 20px;
  129 + }
  130 +
  131 + .stu-rights-item .item-content > p:not(.item-title) {
  132 + text-align: left;
  133 + font-size: 13px;
  134 + line-height: 25px;
  135 + }
113 } 136 }
114 137
115 .stu-identity { 138 .stu-identity {