Showing 61 changed files with 3219 additions and 700 deletions

Too many changes to show.

To preserve performance only 61 of 61+ files are displayed.

/**
* 领券中心 controller
* @author: shenzm<zhimin.shen@yoho.cn>
* @date: 2016/09/29
*/
'use strict';
const couponModel = require('../models/coupon');
exports.index = (req, res, next) => {
const channel = req.cookies._Channel || 'boys';
couponModel.getCouponData(channel, {
uid: req.user.uid,
contentCode: req.query.contentCode
}).then(result => {
res.render('coupon', Object.assign({
module: 'activity',
page: 'coupon'
}, result));
}).catch(next);
};
exports.getCouponStatus = (req, res, next) => {
couponModel.getCouponStatus({
uid: req.user.uid,
contentCode: req.query.contentCode
}).then(result => {
res.json(result);
}).catch(next);
};
exports.sendcoupon = (req, res, next) => {
couponModel.sendcoupon(req.query.id, req.user.uid).then(result => {
res.json(result);
}).catch(next);
};
\ No newline at end of file
... ...
/**
* activity model
* @author: shenzm<zhimin.shen@yoho.cn>
* @date: 2016/09/29
*/
'use strict';
const Promise = require('bluebird');
const api = global.yoho.API;
const crypto = global.yoho.crypto;
const helpers = global.yoho.helpers;
const HeaderModel = require('../../../doraemon/models/header');
const homeService = require('../../product/models/home-service');
exports.getCouponData = (channel, params) => {
return Promise.coroutine(function*() {
const result = {
pathNav: [homeService.getHomeChannelNav(channel), {
name: '领券频道'
}],
footerTop: true,
topBanner: {
list: []
},
categories: []
};
const requestData = yield Promise.all([
api.get('', Object.assign(params, {
method: 'app.promotion.queryCouponCenter'
})),
HeaderModel.requestHeaderData(channel)
]);
const coupon = requestData[0];
result.headerData = requestData[1].headerData;
do {
if (!coupon.data || !Array.isArray(coupon.data) || coupon.data.length === 0) {
break;
}
let i = 0;
coupon.data.forEach(function(val, index) {
// 头部banner
if (val.templateName === 'focus') {
val.data.forEach(function(item) {
result.topBanner.list.push({
href: item.url.replace('http:', ''), // banner跳转链接
img: item.src // banner图片
});
});
} else if (val.template_name === 'getCoupon' && val.data.length) {
// 优惠券楼层
if (!coupon.data[index - 1].data || !coupon.data[index - 1].data.text) {
return;
}
const obj = {
title: coupon.data[index - 1].data.text, // 楼层标题
coupons: []
};
val.data.forEach(function(item) {
obj.coupons.push({
id: crypto.encryption('yoho9646abcdefgh', item.couponID), // 加密优惠券号
img: helpers.image(item.image.src, 0, 0), // 优惠券图片
url: item.image.url.replace('http:', '') // 去逛逛链接
});
});
result.categories.push(obj);
i++;
}
});
}
while (false);
return result;
})();
};
exports.getCouponStatus = (params) => {
return Promise.coroutine(function*() {
const coupon = yield api.get('', Object.assign(params, {
method: 'app.promotion.queryCouponCenter'
}));
const result = {
code: coupon.code,
categories: []
};
do {
if (!coupon.data || !Array.isArray(coupon.data) || coupon.data.length === 0) {
break;
}
let i = 0;
coupon.data.forEach(function(val, index) {
if (val.template_name === 'getCoupon' && val.data.length) {
// 优惠券楼层
if (!coupon.data[index - 1].data || !coupon.data[index - 1].data.text) {
return;
}
val.data.forEach(function(item) {
const status = Number(item.status);
if ([2, 3].indexOf(status) > -1) {
const cou = {
id: crypto.encryption('yoho9646abcdefgh', item.couponID) // 加密优惠券号
};
if (status === 2) {
cou.empty = true; // 优惠券已抢光
} else if (status === 3) {
cou.got = true; // 优惠券已领取
}
result.categories.push(cou);
}
});
i++;
}
});
}
while (false);
return result;
})();
};
exports.sendcoupon = (couponId, uid) => {
let returnData = {};
couponId = crypto.decrypt('yoho9646abcdefgh', couponId);
// 领取优惠券
return api.get('', {
method: 'app.promotion.getCoupon',
couponId: couponId,
uid: uid
}).then(result => {
switch (result.code) {
case 200:
returnData = {
code: 200,
message: '恭喜您,成功领取优惠券',
url: helpers.urlFormat('/home/coupons')
};
break;
case 401:
returnData = {
code: 401,
message: '您已领取过优惠券'
};
break;
case 315:
returnData = {
code: 315,
message: '优惠券已过期'
};
break;
case 300:
returnData = {
code: 300,
message: '请求参数错误'
};
break;
default:
returnData = {
code: 500,
message: '领券失败!'
};
break;
}
return returnData;
});
};
\ No newline at end of file
... ...
... ... @@ -9,10 +9,17 @@
const express = require('express');
const router = express.Router(); // eslint-disable-line
const cRoot = './controllers';
const auth = require(`${global.middleware}/auth`);
const specialController = require(`${cRoot}/special`);
const coupon = require(`${cRoot}/coupon`);
// 专题活动
router.get(/^\/special\/(\d+)_(.*)\.html$/, specialController.special);
module.exports = router;
// 领券中心
router.get('/coupon/index', coupon.index);
router.get('/coupon/couponstatus', coupon.getCouponStatus);
router.get('/coupon/sendcoupon', auth, coupon.sendcoupon);
module.exports = router;
\ No newline at end of file
... ...
<div class="coupon-page yoho-page">
{{> common/path-nav}}
{{> common/slide-banner}}
{{# categories}}
<div class="title clearfix">
<span>{{title}}</span>
</div>
{{# coupons}}
<div class="coupon">
<a href="{{url}}" target="_blank" href="javascript:void(0);" data-id="{{id}}">
<img src="{{img}}">
{{#if empty}}
<div class="coupon-mask"></div>
{{/if}}
<div class="enable info" id="{{id}}">
<div class="normal">
<p>点击</p>
<p>领取</p>
</div>
<div class="got hidden">
<p>已领取</p>
<p class="guang">去使用</p>
</div>
<div class="empty hidden">
<p>已抢光</p>
<p class="guang">去逛逛</p>
</div>
</div>
</a>
</div>
{{/ coupons}}
{{/ categories}}
</div>
\ No newline at end of file
... ...
/**
* 品牌一览 controller
* @author: ghw<hongwei.gao@yoho.cn>
* @date: 2016/9/29
*/
'use strict';
const mRoot = '../models';
const brandsService = require(`${mRoot}/brands-service`); // students model
/**
* brands 首页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.index = (req, res, next) => {
let channel = req.query.channel || req.cookies._Channel || 'boys';
brandsService.getBrandViewList(channel, req).then(result => {
res.render('brands/brands', result);
}).catch(next);
};
/**
* brandList-Ajax调用
*/
exports.brandList = (req, res, next) => {
let channel = req.query.channel || req.cookies._Channel || 'boys';
brandsService.getBrandList(channel, req.body.start).then(result => {
res.render('brands/brand-list', Object.assign({layout: false}, result));
}).catch(next);
};
/**
* 品牌接口数据
*
* @param string brandId 获取品牌ID
* @return json
*/
exports.brandInfo = (req, res, next) => {
let brandId = req.query.brandId || 0;
brandsService.brandInfo(brandId, req.user.uid).then(result => {
res.json(result);
}).catch(next);
};
/**
* 品牌plusstar列表
*/
exports.plusstarList = (req, res, next) => {
let channel = req.query.channel || req.cookies._Channel || 'boys';
brandsService.plusstarList(channel, req).then(result => {
res.render('brands/plusstar', result);
}).catch(next);
};
... ...
/**
* router of sub app brands
* @author: ghw<hongwei.gao@yoho.cn>
* @date: 2016/09/29
*/
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: global.yoho.helpers
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* 品牌一览 api
* @author: ghw<hongwei.gao@yoho.cn>
* @date: 2016/9/29
*/
'use strict';
const api = global.yoho.API;
const serviceAPI = global.yoho.ServiceAPI;
/**
* 分开取数,品牌一览 顶部的轮翻广告及热门品牌数据-PC
* 顶部的轮翻广告及热门品牌数据
* @param string $contentCode 获取广告资源需要的位置码
*/
const getBrandTopData = (contentCode) => {
return serviceAPI.get('operations/api/v5/resource/get', {
content_code: contentCode
}, {cache: 3600});
};
/**
* 分开取数,获取品牌一览 "按字母'A-Z'分组的品牌列表数据"
* @param int $channel 频道标识 1:男,2:女,3:潮童,4:创意生活
*/
const getBrandListData = channel => {
let params = {method: 'app.brand.brandlist'};
if (!isNaN(channel)) {
params.yh_channel = channel;
}
return api.get('', params);
};
/**
* 获取品牌简介
*
* @param integer $brandId 品牌ID
* @param int 用户ID
* @return array 品牌介绍信息
*/
const getBrandIntro = (brandId, uid) => {
let param = {};
if (!uid) {
param.cache = 3600;
}
return api.get('', {
method: 'app.brand.getBrandIntro',
brand_id: brandId,
uid: uid
}, param);
};
/**
* 获取品牌中产品图片
* @param int 品牌ID
* @return array 品牌产品信息
*/
const getProductByBrand = (brandId, limit) => {
return api.get('', {
method: 'web.search.search',
brand: brandId,
limit: limit
});
};
/**
* 获取品牌信息
*
* @param array $ids
* @return array
*/
const getBrandInfoByIds = (ids) => {
return api.get('', {
method: 'web.brand.info',
ids: ids instanceof Array ? ids.join(',') : parseInt(ids, 10)
}, {cache: 3600});
};
/**
* 获取品牌列表
*
* @param int $brandType
* @param string $gender
* @param string $type
* @return array
*/
const getPlusstarList = (brandType, gender) => {
return serviceAPI.get('guang/api/v3/plustar/getlist', {
gender: gender,
brand_type: brandType
}, {cache: 3600});
};
module.exports = {
getBrandTopData,
getBrandListData,
getBrandIntro,
getProductByBrand,
getPlusstarList,
getBrandInfoByIds
};
... ...
/**
* 品牌一览 model
* @author: ghw<hongwei.gao@yoho.cn>
* @date: 2016/9/29
*/
'use strict';
const Promise = require('bluebird');
const co = Promise.coroutine;
const _ = require('lodash');
const helpers = global.yoho.helpers;
const brandApi = require('./brands-api');
const serviceApi = global.yoho.ServiceAPI;
// 品牌一览资源位CODE码
const channelCode = {
boys_brand: '8b16b7baf9a66fbe553a6caa97d2ce2a',
girls_brand: 'c95ae9e40f0add10549b819f821ad626',
kids_brand: 'c575c6bfdfa4125fae7d24bbec7119c8',
lifestyle_brand: '84b7926282fdef92f1039bdcf77c18ba',
brand_list: 'f0f72b1e8f30e6ad086dfc4401f3a856', // 品牌列表资源位CODE码
brand_plusstar_banner_boys: 'd0149783b8dd2adaf083fd10556c39a9',
brand_plusstar_banner_girls: 'aad7a43e9a04ac7c70ae7f0c1acf86ef',
brand_plusstarindex_boys: 'a833aed63d28457156310e97faa7fa37', // plusstarindex男首资源位
brand_plusstarindex_girls: '6e4f162be3b3ba44f3bfcf1c38bdb745' // plusstarindex女首资源位
};
const BOYS = 'boys';
const GIRLS = 'girls';
const KIDS = 'kids';
const LIFESTYLE = 'lifestyle';
/**
* 获取品牌一览资源位&channelType
*
* @param string $channelStr
* @return array
*/
const switchBrandParams = channel => {
let req = {};
switch (channel) {
case BOYS:
req = {
channelType: 1,
brandCode: channelCode.brand_plusstar_banner_boys
};
break;
case GIRLS:
req = {
channelType: 2,
brandCode: channelCode.brand_plusstar_banner_girls
};
break;
case KIDS:
req = {
channelType: 3,
brandCode: channelCode.kids_brand
};
break;
case LIFESTYLE:
req = {
channelType: 4,
brandCode: channelCode.lifestyle_brand
};
break;
default:
req = {
channelType: 1,
brandCode: channelCode.boys_brand
};
break;
}
return req;
};
/**
* 获取品牌一览页面,品牌top
* @param string $channel 频道名称
*/
const getBrandViewTop = channel => {
return co(function*() {
let switchParams = switchBrandParams(channel);
let res = yield brandApi.getBrandTopData(switchParams.brandCode);
let result = {},
brandAds = [],
brandLogos = [];
if (!res || res.code !== 200) {
return result;
}
// 头部10个品牌小图块 url
if (res.data && res.data instanceof Array && res.data[1].data && res.data[1].data.list) {
_.forEach(res.data[1].data.list, subValue => {
brandAds.push({
name: subValue.name,
src: helpers.image(subValue.src, 80, 50, 3),
url: subValue.url
});
});
}
// 头部品牌图块,广告位
if (res.data && res.data instanceof Array && res.data[0].data) {
_.forEach(res.data[0].data, (subValue, k) => {
let srcUrl;
// kids lifestyle 第一张图尺寸不同
if (switchParams.channelType === 1 || switchParams.channelType === 2) {
srcUrl = helpers.image(subValue.src, 222, 180, 3);
} else {
srcUrl = (k === 0) ? helpers.image(subValue.src, 570, 280, 3) :
helpers.image(subValue.src, 280, 280, 3);
}
let brandPlusstarItem = {
name: subValue.title,
src: srcUrl,
url: subValue.url
};
if (channel === BOYS || channel === GIRLS) {
if (k === 0) {
brandPlusstarItem.url = helpers.urlFormat('/brands/plusstar', {channel: channel});
} else {
brandPlusstarItem.url = helpers.urlFormat('/brands/plusstar', {id: k, channel: channel});
}
}
brandLogos.push(brandPlusstarItem);
});
}
// 整合brandTop数据结构,boys、girls
if (switchParams.channelType === 1 || switchParams.channelType === 2) {
result.isTab = true;
}
result.tabHeader = brandLogos;
result.logos = brandAds;
return result;
})();
};
/**
* 获取品牌一览list
* @param string $channel 频道名称
* @param int start 开始位置 1 开始
* @param int length 取数长度 0 取到最后
*/
const getBrandViewList = (channel, start, length) => {
return co(function*() {
let switchParams = switchBrandParams(channel);
let res = yield brandApi.getBrandListData(switchParams.channelType);
let result = [],
navigation = [];
if (!res || res.code !== 200) {
return result;
}
// 品牌list A-Z 0-9
if (res.data && res.data.brands) {
_.forEach(res.data.brands, (subValue, key) => {
let listTmp = [];
_.forEach(subValue, ssubValue => {
// 为品牌名称
let href;
if (switchParams.channelType === 1) {
href = helpers.urlFormat('', {gender: '1,3'}, ssubValue.brand_domain);
} else if (switchParams.channelType === 2) {
href = helpers.urlFormat('', {gender: '2,3'}, ssubValue.brand_domain);
} else {
href = helpers.urlFormat('', '', ssubValue.brand_domain);
}
let brandItem = {
name: ssubValue.brand_name,
key: ssubValue.id,
href: href
};
if (ssubValue.is_hot === 'Y') {
brandItem.hot = 'hot';
}
listTmp.push(brandItem);
});
navigation.push(key);
result.push({
key: key,
val: _.sortBy(listTmp, 'name')// 对name排序
});
});
}
// 只取部分数据
let begin;
if (start) {
begin = (start - 1) ? (start - 1) : 0;
begin = (begin > 0) ? begin : 0;
result = length ? result.slice(begin, length + begin) : result.slice(begin);
}
result.navigation = navigation;
return result;
})();
};
/**
* 获取单个广告浮窗内容
*
* @param int $brandId
* @param int $uid
* @return array
*/
const getBrandInfo = (brandId, uid) => {
return co(function*() {
let data = {},
imgs = [];
// 获取品牌简介
let res = yield brandApi.getBrandIntro(brandId, uid);
if (!res || res.code !== 200) {
return data;
}
if (res.data) {
// 获取品牌下的产品信息
let proInfo = yield brandApi.getProductByBrand(brandId, 3);
if (!proInfo || proInfo.code !== 200) {
return data;
}
let proInfoTmp = proInfo.data.product_list ? proInfo.data.product_list : [];
if (!_.isEmpty(proInfoTmp)) {
_.forEach(proInfoTmp, subValue => {
imgs.push({
src: helpers.image(subValue.default_images, 80, 100, 3)
});
});
}
// 整合
data = {
key: res.data.brand_id,
icon: helpers.image(res.data.brand_ico, 80, 50, 3),
title: res.data.brand_name,
content: res.data.brand_intro,
subtitle: 'FEATURED ITEMS',
imgs: imgs
};
}
return data;
})();
};
/**
* 多个品牌ID获取品牌信息
*
* @param array $brandIds
* @return array
*/
const getBrandInfoByIds = (brandIds) => {
return co(function*() {
let res = yield brandApi.getBrandInfoByIds(brandIds);
let brandsInfo = {};
if (!res || res.code !== 200) {
return brandsInfo;
}
if (res.data && res.code === 200) {
_.forEach(res.data, (subValue, k) => {
subValue.desc = _.trim(subValue.brand_intro.replace(/(\t)|(\n)|(\r)|(&nbsp;)/g, '')
.replace(/<.*?>/ig, ''));
subValue.url = subValue.brand_domain;
delete subValue.brand_intro;
brandsInfo[k] = subValue;
});
}
return brandsInfo;
})();
};
/**
* 获取plusstar品牌列表项目
*
* @param string $channel
* @return array
*/
const getPlusstarBrandListItem = (channel) => {
return co(function*() {
let code = channel === 'girls' ? channelCode.brand_plusstar_banner_girls :
channelCode.brand_plusstar_banner_boys;
// 资源位数据
let resource = yield serviceApi.get('operations/api/v5/resource/get', {content_code: code}, {cache: 3600});
let items = [];
if (!resource || resource.code !== 200) {
return items;
}
if (resource.data && resource.code === 200) {
items[0] = {name: '所有品牌', src: '', url: helpers.urlFormat('/brands', {channel: channel}), brandType: ''};
items[1] = {name: '设计新潮', src: '', url: '', brandType: 4};
items[2] = {name: '潮流经典', src: '', url: '', brandType: 1};
items[3] = {name: '明星潮牌', src: '', url: '', brandType: 2};
items[4] = {name: '原创潮牌', src: '', url: '', brandType: 3};
let resourceData = resource.data,
pos = 0;
_.forEach(items, (subValue, k) => {
if (_.isEmpty(subValue.url)) {
subValue.url = helpers.urlFormat('/brands/plusstar', {id: k, channel: channel});
}
if (pos in resourceData[0].data) {
subValue.src = helpers.image(resourceData[0].data[pos].src, 222, 180, 1);
subValue.name = resourceData[0].data[pos].title;
}
pos++;
});
}
return items;
})();
};
/**
* 获取Plustar列表
*
* @param string $brandType
* @param string $gender
* @return array
*/
const getPlustarList = (brandType, gender) => {
return co(function*() {
let list = yield brandApi.getPlusstarList(brandType, gender);
let brandList = {},
data = {},
brandsIds = [],
result = {brandsIds: [], data: {}};
if (!list || list.code !== 200) {
return result;
}
if (list.data && list.data.data && list.data.data.list) {
brandList = list.data.data.list[0];
}
if (brandList.data) {
_.forEach(brandList.data, brand => {
let src = '';
if (brand.data[0]) {
src = helpers.image(brand.data[0].src, 320, 160, 1);
}
data[brand.brand_id] = {
brand_id: brand.brand_id,
name: brand.brand_name,
sort_id: brand.sort_id,
src: src,
desc: '',
url: ''
};
brandsIds.push(brand.brand_id);
});
result.brandsIds = brandsIds;
result.data = data;
}
return result;
})();
};
module.exports = {
getBrandViewTop,
getBrandViewList,
getBrandInfo,
getBrandInfoByIds,
getPlusstarBrandListItem,
getPlustarList
};
... ...
/**
* 品牌一览 controller
* @author: ghw<hongwei.gao@yoho.cn>
* @date: 2016/9/29
*/
'use strict';
const Promise = require('bluebird');
const co = Promise.coroutine;
const api = global.yoho.API;
const headerModel = require('../../../doraemon/models/header');
const brandsModel = require('./brands-model');
const _ = require('lodash');
const helpers = global.yoho.helpers;
const pager = require(`${global.utils}/pager`).setPager;
const BOYS = 'boys';
const GIRLS = 'girls';
const KIDS = 'kids';
const LIFESTYLE = 'lifestyle';
/**
* 获取品牌一览资源位&channelType
*
* @param string $channelStr
* @return array
*/
const getGenderByChannel = channel => {
let gender = '';
switch (channel) {
case BOYS:
gender = '1,3';
break;
case GIRLS:
gender = '2,3';
break;
default:
gender = '1,2,3';
break;
}
return gender;
};
const getHomeurlByChannel = channel => {
let home;
switch (channel) {
case GIRLS:
home = helpers.urlFormat('/woman', {}, 'new');
break;
case LIFESTYLE:
home = helpers.urlFormat('/lifestyle', {}, 'new');
break;
case KIDS:
home = helpers.urlFormat('/kids', {}, 'new');
break;
default:
home = helpers.urlFormat('');
break;
}
return home;
};
// 添加网站的SEO
const seoMap = {
boys: {
title: '品牌一览|男装品牌排行榜,男装品牌大全|YOHO!BUY 有货 100%正品保证',
keywords: '品牌一览,男装品牌,男装品牌排行榜,男装品牌大全,YOHO!BUY 有货',
description: 'YOHO!BUY 有货男装品牌一览汇集国内国际各大男装品牌大全,为广大爱好时尚的男士青年提供品牌男装、' +
'休闲男装、商务男装.YOHO!BUY 有货,100%正品保证'
},
girls: {
title: '品牌一览|女装品牌排行榜,女装品牌大全|YOHO!BUY 有货 100%正品保证',
keywords: '品牌一览,女装品牌,女装品牌排行榜,女装品牌大全,YOHO!BUY 有货',
description: 'YOHO!BUY 有货女装品牌一览汇集国内国际各大女装品牌,为广大爱美女生提供品牌女装、休闲女装、' +
'商务女装.买品牌女装就上YOHO!BUY 有货,100%正品保证'
},
kids: {
title: '品牌一览|童装童鞋品牌,儿童鞋包配饰排行榜,潮童品牌大全|YOHO!BUY 有货 100%正品保证',
keywords: '童装品牌,童装童鞋排行榜,儿童鞋包配饰排行榜,潮童品牌大全,品牌一览,YOHO!BUY 有货',
description: 'YOHO!BUY 有货童装品牌一览汇集国内国际各大童装品牌大全,为广大爱好潮流的儿童提供品牌童装、童鞋,' +
'儿童鞋包配饰.YOHO!BUY 有货,100%正品保证'
},
lifestyle: {
title: '品牌一览|数码3c,居家,玩具娱乐,文具,美妆品牌|YOHO!BUY 有货 100%正品保证',
keywords: '数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,美妆品牌',
description: 'YOHO!BUY 有货女装品牌一览汇集国内国际各大数码3c品牌,居家品牌,玩具娱乐品牌,文具品牌,' +
'美妆品牌.买创意生活家居就上YOHO!BUY 有货,100%正品保证'
}
};
/**
* 获取品牌一览list
* @param string $channel 频道名称
* @param int start 开始位置 1 开始
* @param int length 取数长度 0 取到最后
*/
exports.getBrandViewList = (channel) => {
let apiMethod = [
headerModel.requestHeaderData(channel),
brandsModel.getBrandViewTop(channel),
brandsModel.getBrandViewList(channel, 1, 5) // 分屏加载
];
return api.all(apiMethod).then(result => {
let responseData = {
module: 'brands',
page: 'brands'
};
// 头部数据
Object.assign(responseData, result[0]);
// 品牌一览列表
responseData.brands = result[1];
responseData.brands.navigation = result[2].navigation;
responseData.brands.category = result[2];
// 导航pathNav
responseData.brands.pathNav = [
{
href: getHomeurlByChannel(channel),
name: `${_.toUpper(channel)}首页`,
pathTitle: 'YOHO!BUY 有货'
},
{
href: helpers.urlFormat('/brands'),
name: '品牌一览',
pathTitle: '品牌一览'
}
];
// SEO
Object.assign(responseData, seoMap[channel]);
return responseData;
});
};
/**
* brandList-Ajax调用
*/
exports.getBrandList = (channel, start) => {
let apiMethod = [
brandsModel.getBrandViewList(channel, start)
];
return api.all(apiMethod).then(result => {
let responseData = {};
// 品牌一览列表
responseData.category = result[0];
return responseData;
});
};
/**
* 品牌接口数据
*
* @param string brandId 获取品牌ID
* @return json
*/
exports.brandInfo = (brandId, uid) => {
let apiMethod = [
brandsModel.getBrandInfo(brandId, uid)
];
return api.all(apiMethod).then(result => {
return {
code: _.isEmpty(result[0]) ? 400 : 200,
brand: _.isEmpty(result[0]) ? '' : result[0]
};
});
};
/**
* 多个品牌ID获取品牌信息
*
* @param array $brandIds
* @return array
*/
exports.plusstarList = (channel, req) => {
return co(function*() {
let headerData = yield headerModel.requestHeaderData(channel);
let responseData = {
module: 'brands',
page: 'brands',
brandsHomePage: true
};
let id = req.query.id || '',
gender = req.query.gender || getGenderByChannel(channel),
limit = 20,
page = parseInt(req.query.page, 10) || 1;
let items = yield brandsModel.getPlusstarBrandListItem(channel);
let brandType = 1;
if (items[id]) {
brandType = items[id].brandType;
}
let plustarList = yield brandsModel.getPlustarList(brandType, gender);
let list = plustarList.data;
let brandIds = [],
brands = [],
pageList = {};
brandIds = plustarList.brandsIds.slice((page - 1) * limit, page * limit);
if (plustarList.brandsIds.length > limit) {
pageList = pager(Math.ceil(plustarList.brandsIds.length / limit), {
channel: channel,
page: page,
id: id
});
}
if (brandIds.length > 0) {
// 获取品牌信息
let brandsInfo = yield brandsModel.getBrandInfoByIds(brandIds);
_.forEach(brandIds, brandId => {
if (brandsInfo[brandId]) {
list[brandId].desc = brandsInfo[brandId].desc;
list[brandId].url = helpers.urlFormat('', {gender: gender}, brandsInfo[brandId].url);
}
brands.push(list[brandId]);
});
}
let data = {
brandsHomePage: true,
brands: {
items: brands,
tabs: items
}
};
// 头部数据
Object.assign(responseData, headerData);
// 产品信息
Object.assign(responseData, data);
// 页码
Object.assign(responseData, pageList);
// SEO
Object.assign(responseData, seoMap[channel]);
return responseData;
})();
};
... ...
/**
* router of sub app brands
* @author: ghw<hongwei.gao@yoho.cn>
* @date: 2016/09/29
*/
'use strict';
const express = require('express');
const cRoot = './controllers';
const router = express.Router(); // eslint-disable-line
const brandsController = require(`${cRoot}/brands`);
// 品牌一览
router.get('', brandsController.index);
// 悬浮出现品牌信息
router.get('/brandinfo', brandsController.brandInfo);
// 品牌没有加载完全,继续加载
router.post('/brandList', brandsController.brandList);
// brands/plusstar
router.get('/plusstar', brandsController.plusstarList);
module.exports = router;
... ...
<div class="home-page yoho-page brands" data-page="brands">
{{# brands}}
{{> common/path-nav}}
{{#if isTab}}
<div class="brands-tabs">
<ul class="clearfix">
{{#each tabHeader}}
<li>
<a href="{{url}}">
<div class="g-mask"></div>
<p class="tips">{{name}}</p>
<img class="lazy" data-original="{{src}}"/>
</a>
</li>
{{/each}}
</ul>
<div class="hover-contain">
<div class="hoverarr">
<i></i>
</div>
</div>
</div>
{{^}}
<ul class="brands-ad clearfix">
{{#each tabHeader}}
<li>
<a href="{{url}}" target="_blank">
<img class="lazy" data-original="{{src}}">
</a>
</li>
{{/each}}
</ul>
{{/if}}
<div class="brands-logo clearfix">
{{#each logos}}
<a href="{{url}}" title="{{name}}" target="_blank">
<img class="lazy" data-original="{{src}}">
</a>
{{/each}}
</div>
<div class="brands-category">
<div class="category-nav">
<span>BRANDS A-Z:</span>
{{#each navigation}}
<a href="#{{this}}">{{this}}</a>
{{/each}}
</div>
</div>
<div class="brands-list" >
{{> brand-list}}
</div>
{{/ brands}}
</div>
... ...
{{> layout/header}}
<div class="home-page yoho-page brands" data-page="brands">
{{# brands}}
{{! 头部banner}}
{{# slide}}
{{>index/slide-banner}}
{{/ slide}}
{{! 品牌 BRAND}}
{{# brand}}
{{> index/floor-header}}
<div class="brandfloor clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{url}}" target= "_blank">
<img class="lazy" data-original="{{src}}" alt="">
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ brand}}
{{! 单品 SINGLE GOODS}}
{{# singlegoods}}
{{> index/floor-header}}
<div class="singlegoods clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{url}}" target= "_blank">
<img class="lazy" data-original="{{src}}" alt="">
<div class="singlegoods-title">
<div class="g-mask"></div>
<p>{{name}}</p>
</div>
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ singlegoods}}
{{!视频 VIDEO}}
{{# video}}
{{> index/floor-header}}
<div class="video clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{url}}" target= "_blank">
<img class="lazy" data-original="{{src}}" alt="" /><i class="video-play"></i>
<div class="video-title">
<div class="g-mask"></div>
<p>{{name}}</p>
</div>
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ video}}
{{!新闻 NEWS}}
{{# news}}
{{> index/floor-header}}
<div class="news clearfix">
<div class="news-pic">
{{# pics}}
{{>index/slide-banner}}
{{/ pics}}
</div>
<div class="news-txt">
{{# txts}}
<ul>
{{#each list}}
<li>
<i class="iconfont">&#xe619;</i><a href="{{url}}">{{name}}</a>
</li>
{{/each}}
</ul>
{{/ txts}}
</div>
</div>
{{/ news}}
{{!推广 AD}}
{{# ads}}
<div class="ads clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{url}}" target= "_blank">
<img class="lazy" data-original="{{src}}" alt="">
<span class="name g-title">{{name}}</span>
<span class="des g-title">{{des}}</span>
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ ads}}
{{/ brands}}
</div>
{{> layout/footer}}
\ No newline at end of file
... ...
{{> layout/header}}
<div class="home-page yoho-page brands" data-page="brands">
{{# brands}}
<div class="sit-nav">
<a href="#">BOYS首页</a><span class="sep">></span><a href="#">品牌一览</a>
</div>
<div class="brands-tabs height-initial">
<ul class="clearfix">
{{#each tabs}}
<li>
<a href="{{url}}" target="_blank">
<div class="g-mask"></div>
<p class="tips">{{name}}</p>
<img class="lazy" data-original="{{src}}"/>
</a>
</li>
{{/each}}
</ul>
<div class="hover-contain">
<div class="hoverarr">
<i></i>
</div>
</div>
</div>
<div class="brands-items clearfix">
{{#each items}}
<div class="brands-item clearfix">
<a class="brands-pic" title="{{name}}" href="{{url}}" target="_blank">
<img class="lazy" data-original="{{src}}"/>
</a>
<div class="brand-info">
<a title="{{name}}" href="{{url}}" target="_blank">
<h3>
{{name}}
</h3>
</a>
<div class="brand-desc">{{desc}}</div>
</div>
</div>
{{/each}}
</div>
<div class="pagination">
<a href="#" class="page_pre" title="上一页"><i class="iconfont">&#xe60f;</i>上一页</a>
<a href="#"><span>1</span></a>
<a href="#" class="cur"><span>2</span></a>
<a href="#"><span>3</span></a>
<a href="#"><span>4</span></a>
<a href="#"><span>5</span></a>
<a><span>...</span></a>
<a href="#"><span>215</span></a>
<a href="#" title="下一页">下一页<i class="iconfont">&#xe60e;</i></a>
</div>
{{/ brands}}
</div>
{{> layout/footer}}
\ No newline at end of file
... ...
<div class="home-page yoho-page brands" data-page="brands">
{{# brands}}
{{> common/path-nav}}
<div class="brands-tabs height-initial">
<ul class="clearfix">
{{#each tabs}}
<li>
<a href="{{url}}">
<div class="g-mask"></div>
<p class="tips">{{name}}</p>
<img class="lazy" data-original="{{src}}"/>
</a>
</li>
{{/each}}
</ul>
<div class="hover-contain">
<div class="hoverarr">
<i></i>
</div>
</div>
</div>
<div class="brands-items clearfix">
{{#each items}}
<div class="brands-item clearfix">
<a class="brands-pic" title="{{name}}" href="{{url}}" target="_blank">
<img class="lazy" data-original="{{src}}"/>
</a>
<div class="brand-info">
<a title="{{name}}" href="{{url}}" target="_blank">
<h3>
{{name}}
</h3>
</a>
<div class="brand-desc">{{{desc}}}</div>
</div>
</div>
{{/each}}
</div>
{{/ brands}}
<div class="pagination clearfix">
{{# prePage}}
<a href="{{url}}" title="上一页">上一页<span class="iconfont">&#xe60e;</span></a>
{{/ prePage}}
{{# pages}}
<a{{#if url}} href="{{url}}"{{/if}}{{#if cur}} class="cur"{{/if}}>{{num}}</a>
{{/ pages}}
{{# nextPage}}
<a href="{{url}}" title="下一页">下一页<span class="iconfont">&#xe60c;</span></a>
{{/ nextPage}}
</div>
</div>
... ...
{{> layout/header}}
<div class="home-page yoho-page brands" data-page="brands">
{{# brands}}
{{! 头部banner}}
{{# slide}}
{{>index/slide-banner}}
{{/ slide}}
{{! 品牌 BRAND}}
{{# brand}}
{{> index/floor-header}}
<div class="brandfloor list-floor clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{href}}" target= "_blank">
<img class="lazy" data-original="{{img}}" alt="">
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ brand}}
{{! 单品 SINGLE GOODS}}
{{# singlegoods}}
{{> index/floor-header}}
<div class="singlegoods list-floor clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{href}}" target= "_blank">
<img class="lazy" data-original="{{img}}" alt="">
<div class="singlegoods-title">
<div class="g-mask"></div>
<p>{{name}}</p>
</div>
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ singlegoods}}
{{!视频 VIDEO}}
{{# video}}
{{> index/floor-header}}
<div class="video list-floor clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{href}}" target= "_blank">
<img class="lazy" data-original="{{img}}" alt="" /><i class="video-play"></i>
<div class="video-title">
<div class="g-mask"></div>
<p>{{name}}</p>
</div>
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ video}}
{{!新闻 NEWS}}
{{# news}}
{{> index/floor-header}}
<div class="news clearfix">
<div class="news-pic">
{{# pics}}
{{>index/slide-banner}}
{{/ pics}}
</div>
<div class="news-txt">
{{# txts}}
<ul>
{{#each list}}
<li>
<i class="iconfont">&#xe619;</i><a href="{{href}}" target= "_blank">{{name}}</a>
</li>
{{/each}}
</ul>
{{/ txts}}
</div>
</div>
{{/ news}}
{{!推广 AD}}
{{# ads}}
<div class="ads list-floor clearfix">
<ul class="g-list">
{{# list}}
<li>
<a href="{{href}}" target= "_blank">
<img class="lazy" data-original="{{img}}" alt="">
<span class="name g-title">{{name}}</span>
<span class="des g-title">{{des}}</span>
</a>
</li>
{{/ list}}
</ul>
</div>
{{/ ads}}
{{/ brands}}
</div>
{{> layout/footer}}
\ No newline at end of file
... ...
{{#each category}}
<dl class="clearfix" name="{{key}}">
<dt>{{key}}</dt>
<dd>
<ul class="clearfix">
{{#each val}}
<li>
<a class="{{hot}}" data-key="{{key}}" href="{{href}}" target="_blank">
<span>{{name}}</span>
</a>
{{# hot}}
<i class="iconfont">&#xe62c;</i>
{{/ hot}}
</li>
{{/each}}
</ul>
</dd>
</dl>
{{/each}}
... ...
/**
* Created by TaoHuang on 2016/10/19.
*/
'use strict';
const service = require('../models/cart-service');
const getProductInfo = (req, res, next) => {
let pid = req.query.productId || '';
service.getProductInfoAsync(pid).then((result) => {
return res.render('goods-detail', Object.assign({
layout: false
}, result));
}).catch(next);
};
module.exports = {
getProductInfo
};
... ...
/**
* sub app cart
* @author: htoooth<ht.anglenx@gmail.com>
* @date: 2016/10/19
*/
'use strict';
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); //parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: global.yoho.helpers
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* 商品详情models
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/5/6
*/
'use strict';
const Promise = require('bluebird');
const co = Promise.coroutine;
const _ = require('lodash');
const helpers = global.yoho.helpers;
const productAPI = require('./product-api');
const _getProductIntroAsync = (productSkn) => {
return co(function * () {
let result = yield Promise.props({
sizeInfo: productAPI.sizeInfoAsync(productSkn)
});
return result;
})();
};
/**
* 获得sku商品数据
*/
const _getSkuDataByProductBaseInfo = (data) => {
let totalStorageNum = 0;
let skuGoods = null;// sku商品
let defaultImage = '';// 默认图
let chooseSkuFlag = false; // 选中状态
if (_.isEmpty(_.get(data, 'goods_list', []))) {
return {
totalStorageNum,
skuGoods,
defaultImage
};
}
skuGoods = _.get(data, 'goods_list', []).reduce((acc, cur, pos)=> {
// 如果status为0,即skc下架时就跳过该商品$value['status'] === 0
let goodsGroup = {};
if (_.isEmpty(cur.color_image)) {
return acc;
}
if (cur.images_list) {
// 商品列表
goodsGroup.productSkc = cur.product_skc;
goodsGroup.src = helpers.image(cur.color_image, 40, 40);
goodsGroup.title = `${_.trim(data.product_name)} ${cur.color_name}`;
goodsGroup.name = cur.color_name;
goodsGroup.focus = false;
goodsGroup.total = 0;
goodsGroup.thumbs = [];
goodsGroup.size = [];
}
_.get(cur, 'images_list', []).forEach((good) => {
if (good.image_url) {
goodsGroup.thumbs.push({
url: '',
shower: helpers.image(good.image_url, 420, 560),
img: helpers.image(good.image_url, 75, 100)
});
}
});
// 缩略图空,不显示
if (_.isEmpty(goodsGroup.thumbs)) {
return acc;
}
// 默认第一张图片
if (pos === 0) {
defaultImage = helpers.image(cur.color_image, 420, 560);
}
// 商品的尺码列表
_.get(cur, 'size_list', []).forEach((size) => {
if (data.attribute === 3) {
// 虚拟商品,门票默认最大为4,
size.storage_number = size.storage_number > 4 ? 4 : size.storage_number;
}
// 如果status为0,即skc下架时就跳过该商品
if (cur.status === 0) {
size.storage_number = 0;
}
goodsGroup.size.push({
name: size.size_name,
sku: size.product_sku,
num: _.parseInt(size.storage_number),
goodsId: size.size_id
});
// 单个sku商品的总数
goodsGroup.total += _.parseInt(size.storage_number);
if (goodsGroup.total > 0 && !chooseSkuFlag) { // 默认选中该sku商品
goodsGroup.focus = true;
chooseSkuFlag = true;// 选中sku商品
}
totalStorageNum += _.parseInt(size.storage_number);
});
acc.push(goodsGroup);
return acc;
}, []);
if (!_.isEmpty(skuGoods) && !chooseSkuFlag) { // 没有选中一个sku商品,默认选中第一个sku商品
_.head(skuGoods).focus = true;
}
return {
defaultImage: defaultImage,
skuGoods: skuGoods,
totalStorageNum: totalStorageNum
};
};
/**
* 使sizeBoList id以 sizeAttributeBos id顺序一样
* @param sizeInfoBo
*/
const _sizeInfoBoSort = (sizeInfoBo) => {
if (!sizeInfoBo.sizeBoList || !sizeInfoBo.sizeAttributeBos) {
return {};
}
_.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> {
let sortAttr = {};
sizeBoList.sortAttributes.forEach(sortAttributes => {
sortAttr[sortAttributes.id] = sortAttributes;
});
sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
});
_.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> {
let sortAttr = [];
sizeInfoBo.sizeAttributeBos.forEach(val => {
if (sizeBoList.sortAttributes[val.id]) {
sortAttr.push(sizeBoList.sortAttributes[val.id]);
}
});
sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
});
return sizeInfoBo;
};
/**
* 获取尺寸信息
* @param sizeInfo
* @returns {{}}
*/
const _getSizeData = (sizeInfo) => {
// 尺码信息
if (!_.has(sizeInfo, 'sizeInfoBo')) {
return {};
}
sizeInfo.sizeInfoBo = _sizeInfoBoSort(sizeInfo.sizeInfoBo);
let boyReference = _.get(sizeInfo, 'productExtra.boyReference', false);
let girlReference = _.get(sizeInfo, 'productExtra.girlReference', false);
let gender = _.get(sizeInfo, 'productDescBo.gender', 3);
let referenceName = (function() {
if (gender === 3 && boyReference) {
return '参考尺码(男)';
} else if (gender === 3 && girlReference) {
return '参考尺码(女)';
} else {
return '参考尺码';
}
}());
// 判断是否显示参考尺码
let showReference = (boyReference && _.get(sizeInfo, 'sizeInfoBo.sizeBoList[0].boyReferSize', false)) ||
(girlReference && _.get(sizeInfo, 'sizeInfoBo.sizeBoList[0].girlReferSize', false));
if (!_.has(sizeInfo, 'sizeInfoBo.sizeAttributeBos')) {
return {};
}
// 尺码信息头部
let size = {
thead: [{name: '吊牌尺码', id: ''}],
tbody: []
};
// 显示参考尺码
if (showReference) {
size.thead[1] = {name: referenceName, id: ''};
}
_.get(sizeInfo, 'sizeInfoBo.sizeAttributeBos', []).forEach((value) => {
size.thead.push({
name: value.attributeName || ' ',
id: value.id
});
});
_.get(sizeInfo, 'sizeInfoBo.sizeBoList', []).forEach((value) => {
let sizes = [];
// 吊牌尺码
sizes.push(value.sizeName);
// 判断是否显示参考尺码
if (boyReference && (gender === 1 || gender === 3) && showReference) {
sizes.push(_.get(value, 'boyReferSize.referenceName', ' '));
} else if (girlReference && (gender === 2 || gender === 3) && showReference) {
sizes.push(_.get(value, 'girlReferSize.referenceName', ' '));
} else {
if (size.thead[1] && showReference) {
size.thead[1] = {};
}
}
// 其他尺码信息
_.get(value, 'sortAttributes', []).forEach(attr => {
sizes.push(_.get(attr, 'sizeValue', ' '));
});
// 尺码信息
size.tbody.push(sizes);
});
// 参考尺码为空
if (_.isEmpty(size.thead[1]) && showReference) {
// 移除这个值
size.thead.splice(1, 1);
}
// 测量方式
if (sizeInfo.sizeImage) {
size.sizeImg = sizeInfo.sizeImage;
}
return size;
};
/**
* 商品尺码信息
*
* @param productSkn
* @param maxSortId
* @return object
*/
const _getIntroInfo = (productSkn, additionalData)=> {
if (!productSkn) {
return {};
}
let sizeInfo = additionalData.sizeInfo;
if (_.isEmpty(sizeInfo)) {
return {};
}
let result = {};
// 尺寸数据
result.size = _getSizeData(sizeInfo);
return result;
};
/**
* 详情页数据格式化
* @param origin Object 原始数据
* @return result Object 格式化数据
*/
const _detailDataPkg = (origin) => {
return co(function*() {
if (_.isEmpty(origin) || _.isEmpty(origin)) {
return {};
}
let result = {};
let propOrigin = _.partial(_.get, origin);
// 商品名称
if (!propOrigin('product_name')) {
return result;
}
result.name = propOrigin('product_name');
result.skn = propOrigin('product_skn');
result.productId = propOrigin('product_id');
// 商品价格
result.marketPrice = propOrigin('format_market_price');
result.salePrice = propOrigin('format_sales_price');
result.hasOtherPrice = true;
if (result.salePrice === '0') {
delete result.salePrice;
result.hasOtherPrice = false;
}
// 上市期
if (propOrigin('expect_arrival_time')) {
result.arrivalDate = `${propOrigin('expect_arrival_time')}月`;
result.presalePrice = propOrigin('format_sales_price');
delete result.salePrice;
result.hasOtherPrice = false;
}
// sku商品信息
let skuData = _getSkuDataByProductBaseInfo(origin);
// 商品购买状态
let soldOut = !!(propOrigin('status') === 0 || skuData.totalStorageNum === 0);
let notForSale = propOrigin('attribute') === 2; // 非卖品
let virtualGoods = propOrigin('attribute') === 3; // 虚拟商品
if (!soldOut && !notForSale && !virtualGoods) {
result.addToCart = 1;
}
result.colors = skuData.skuGoods;
return result;
})();
};
/**
* 获取某一个商品详情主页面
*/
const getProductInfoAsync = (pid) => {
return co(function * () {
if (!pid) {
return {};
}
// 获取商品基本信息
let productData = yield productAPI.getProductAsync(pid);
if (_.isEmpty(productData.data)) {
return Promise.reject({
code: 404,
message: 'app.product.data api wrong'
});
}
let productSkn = _.get(productData, 'data.product_skn');
let requestData = yield Promise.all([
_getProductIntroAsync(productSkn), // 商品详细介绍
_detailDataPkg(productData.data) // 商品详细价格
]);
let productDescription = requestData[0];
let productInfo = requestData[1];
let intro = _getIntroInfo(productSkn, productDescription);
return Object.assign(productInfo, intro);
})();
};
module.exports = {
getProductInfoAsync // 获取某一个商品详情主页面
};
... ...
/**
* Created by TaoHuang on 2016/10/19.
*/
const api = global.yoho.API;
const sizeInfoAsync = skn => {
return api.get('', {
method: 'h5.product.intro',
productskn: skn
});
};
/**
* 获得产品信息
* @param pid
* @returns {Promise.<type>}
*/
const getProductAsync = (pid) => {
return api.get('', {
method: 'app.product.data',
product_id: pid
});
};
module.exports = {
sizeInfoAsync,
getProductAsync
};
... ...
/**
* router of sub app cart
* @author: htoooth<ht.anglenx@gmail.com>
* @date: 2016/10/19
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const cart = require(`${cRoot}/cart`);
router.get('/index/getProductInfo', cart.getProductInfo);
// Your controller here
module.exports = router;
... ...
<div class="detail-header">
<span class="colse">X关闭</span>
</div>
<div class="detail-body">
<span class="magnify"></span>
{{#colors}}
<div class="detail-bigpic {{#unless focus}}none{{/unless}}">
{{#thumbs}}
<div class="bigpic">
<img src="{{shower}}">
</div>
{{/thumbs}}
<div class="piclist">
<span class="pre"></span>
<div class="con">
<ul>
{{#thumbs}}
<li><img src="{{img}}"></li>
{{/thumbs}}
</ul>
</div>
<span class="next"></span>
</div>
</div>
{{/colors}}
<div class="detail-info">
<div class="title">
<h2>{{name}}</h2>
</div>
<div class="type">
<span class="type-s">新品</span>
</div>
<div class="price">
{{#if salePrice}}
<span class="oldprice">原价:<del>¥{{marketPrice}}</del></span>
<span class="newprice">现价:<b class="promotion-price">¥{{salePrice}}</b></span>
{{^}}
<span class="newprice {{#presalePrice}}none{{/presalePrice}}">原价:<b class="promotion-price">¥{{marketPrice}}</b></span>
{{/if}}
{{#if presalePrice}}
<span class="oldprice">原价:<del>¥{{marketPrice}}</del></span>
<span class="newprice">预售价:<b class="promotion-price">¥{{presalePrice}}</b></span>
{{/if}}
{{#arrivalDate}}
<span class="arrivalDate">上市期:{{arrivalDate}}</span>
{{/arrivalDate}}
</div>
<div class="order">
<dl>
<dd class="colorBox">选颜色:</dd>
<dt>
<div class="colorBox">
<ul>
{{#colors}}
<li class="color">
<p class="{{#if focus}}atcive{{/if}}"><span></span><img src="{{src}}"></p>
<span>{{name}}</span>
</li>
{{/colors}}
</ul>
</div>
</dt>
<dd class="">选尺码:</dd>
<dt>
{{#colors}}
<div class="showSizeBox {{#unless focus}}none{{/unless}}">
{{#size}}
<span data-sku="{{sku}}" data-num="{{num}}">{{name}}</span>
{{/size}}
</div>
{{/colors}}
</dt>
<dd>选件数:</dd>
<dt>
<div class="amount_wrapper">
<i class="amount cut"></i>
<input type="text" id="mnum" class="mnum" value="1" readonly="readonly">
<i class="amount add"></i>
</div>
</dt>
</dl>
</div>
<div class="submit">
<input class="addcart" type="button">
<input class="btn_pre_sale none" type="button">
<input class="btn_sellout none" type="button">
<input class="fav_count" type="button">
</div>
</div>
<div class="detail-size">
<h3>尺码信息<span>(单位:厘米)</span></h3>
{{# size}}
<table>
<thead>
<tr>
{{# thead}}
<td width="{{width}}">{{name}}</td>
{{/ thead}}
</tr>
</thead>
<tbody>
{{# tbody}}
<tr>
{{#each .}}
<td>{{.}}</td>
{{/each}}
</tr>
{{/ tbody}}
</tbody>
</table>
{{/ size}}
<div class="size-info">
※ 以上尺寸为实物实际测量,因测量方式不同会有略微误差,相关数据仅作参考,以收到实物为准。
</div>
</div>
</div>
<input value="{{addToCart}}" id="addToCart" type="hidden" />
... ...
/**
* 最近浏览controller
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/10/11
*/
'use strict';
const rvModel = require('../models/recent-view');
const _ = require('lodash');
const index = (req, res, next) => {
let limit = req.query.limit;
let browserSkn = decodeURIComponent(req.cookies._browseskn);
// 拆解skn
let skn = browserSkn ? browserSkn.replace(/\-(\d)+(\,){0,1}/g, ',') : '';
// 去除skn字符串的最后一个多余的,
if (skn && skn.lastIndexOf(',') === skn.length - 1) {
skn = skn.slice(0, -1);
}
if (!skn) {
res.jsonp({
code: 200,
data: [],
message: 'User info'
});
} else {
skn = _.slice(_.uniq(skn.split(',')), 0, limit).join(','); // 去重+截取
rvModel.index(skn, limit).then(data => {
res.jsonp(data);
}).catch(next);
}
};
module.exports = {
index
};
... ...
/**
* sub app common
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/10/11
*/
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: global.yoho.helpers
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* recent view model
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/10/11
*/
'use strict';
const _ = require('lodash');
const api = global.yoho.API;
const helper = global.yoho.helpers;
const index = (skn, limit) => {
return api.get('', {
method: 'h5.product.batch',
productSkn: skn,
limit: limit
}).then(result => {
if (result.code === 200) {
let data = [];
let historyProduct = result.data.product_list;
_.forEach(historyProduct, hp => {
if (!hp) {
return;
}
let mp = hp.market_price;
let sp = hp.sales_price;
let defaultGoods = _.find(hp.goods_list, {is_default: 'Y'});
// 无默认商品取商品列表第一个
if (!defaultGoods) {
defaultGoods = hp.goods_list[0];
}
data.push({
market_price: mp === sp ? '' : ${helper.round(mp, 2)}`,
price: ${helper.round(sp, 2)}`,
product_name: hp.product_name,
url: helper.urlFormat(
`/product/pro_${hp.product_id}_${defaultGoods.goods_id}/${hp.cn_alphabet}.html`, '', 'item'),
pic_url: helper.image(defaultGoods.images_url, 150, 200, 2, 70)
});
});
return {
code: 200,
data: data,
message: result.message
};
} else {
// get list error
return {
code: result.code,
message: result.message,
data: []
};
}
});
};
module.exports = {
index
};
\ No newline at end of file
... ...
/**
* router of sub app common
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/10/11
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const rvCtrl = require(`${cRoot}/recent-view`);
router.get('/recentReview', rvCtrl.index); // 最近浏览
module.exports = router;
... ...
... ... @@ -15,6 +15,14 @@ const _ = require('lodash');
* 找回密码主页面
*/
const index = (req, res, next) => {
// 清除cookie
res.clearCookie('_UID', {
domain: 'yohobuy.com'
});
res.clearCookie('_TOKEN', {
domain: 'yohobuy.com'
});
service.indexPageDataAsync()
.then(result => {
res.render('back/index', Object.assign({
... ...
... ... @@ -33,38 +33,33 @@ function doPassportCallback(req, res, user) {
refer = config.siteUrl;
}
if (user.openId) {
let signinByOpenID;
user.nickname = _.trim(user.nickname);
if (user.sourceType === 'wechat') {
// PC 的微信登录之前使用了 open_id, 所以需要特别的接口处理
signinByOpenID = AuthHelper.signinByWechat(
user.nickname, user.openId, user.unionId, user.sourceType, shoppingKey);
} else {
signinByOpenID = AuthHelper.signinByOpenID(
user.nickname, user.openId, user.sourceType, shoppingKey);
// PC 的微信登录使用unionId
user.openId = user.unionId;
}
return signinByOpenID.then((result) => {
if (result.code !== 200) {
return Promise.reject(result);
}
if (result.data['is_bind'] && result.data['is_bind'] === 'N') { //eslint-disable-line
return helpers.urlFormat('/passport/thirdlogin/index', {
openId: user.unionId || user.openId,
sourceType: user.sourceType,
refer: refer
});
} else if (result.code === 200 && result.data.uid) {
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return refer;
});
}
}).then((redirectTo) => {
return res.redirect(redirectTo);
});
return AuthHelper.signinByOpenID(user.nickname, user.openId, user.sourceType, shoppingKey)
.then((result) => {
if (result.code !== 200) {
return res.redirect(config.siteUrl);
}
if (result.data['is_bind'] && result.data['is_bind'] === 'N') { //eslint-disable-line
return helpers.urlFormat('/passport/thirdlogin/index', {
openId: user.unionId || user.openId,
sourceType: user.sourceType,
refer: refer
});
} else if (result.code === 200 && result.data.uid) {
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return refer;
});
}
})
.then((redirectTo) => {
return res.redirect(redirectTo);
});
} else {
return Promise.resolve(res.redirect(loginPage));
}
... ... @@ -75,7 +70,7 @@ const common = {
let refer = req.query.refer || req.get('Referer');
refer && res.cookie('refer', encodeURI(refer), {
domain: 'yohobuy.com'
domain: '.yohobuy.com'
});
next();
},
... ... @@ -122,11 +117,7 @@ const local = {
areaName = area ? area.name : '';
}
let refer = req.query.refer;
refer && res.cookie('refer', encodeURI(refer), {
domain: 'yohobuy.com'
});
let refer = req.query.refer || req.cookies.refer || req.get('Referer');
PassportHelper.getLeftBannerAsync(SIGNIN_LEFT_BANNER_CODE).then(cover => {
res.render('login', {
... ... @@ -176,13 +167,13 @@ const local = {
let refer = (function() {
if (/sign|login|reg|passport/.test(_.get(req, 'cookies.refer', ''))) {
return `${config.siteUrl}/home`;
} else if (_.has(req, 'cookies.refer')) {
} else if (_.get(req, 'cookies.refer')) {
return decodeURI(req.cookies.refer);
} else {
return `${config.siteUrl}/home`;
}
}());
console.log(user.uid);
AuthHelper.syncUserSession(user.uid, req, res).then(() => {
res.json({
code: 200,
... ...
... ... @@ -6,6 +6,11 @@ const aes = require('./aes-pwd');
const cache = global.yoho.cache;
const sign = global.yoho.sign;
const api = global.yoho.API;
const cookie = global.yoho.cookie;
const Promise = require('bluebird');
const cartService = require('./cart-service');
const Auth = {
signin(type, area, profile, password, shoppingKey) {
... ... @@ -137,12 +142,11 @@ const Auth = {
return api.get('', param);
},
syncUserSession(uid, req, res) {
return Auth.profile(uid).then((userInfo) => {
return Promise.all([Auth.profile(uid), cartService.goodsCount(uid)]).spread((userInfo, count) => {
let token = sign.makeToken(uid);
let data = userInfo.data;
let encryptionUid = aes.encryptionUid(data.uid);
if (data) {
let uidCookie = `${data.profile_name}::${encryptionUid}::${data.vip_info.title}::${token}`;
let isStudent = data.vip_info.is_student || 0;
... ... @@ -156,6 +160,16 @@ const Auth = {
res.cookie('isStudent', isStudent, {
domain: 'yohobuy.com'
});
// 购物车中商品的数量
res.cookie('_g', JSON.stringify({
_k: cookie.getShoppingKey(req),
_nac: count,
_ac: 0,
_c: 1
}), {
domain: 'yohobuy.com'
});
}
req.session._TOKEN = token; // esline-disable-line
req.session._LOGIN_UID = uid; // esline-disable-line
... ...
/**
* Created by TaoHuang on 2016/9/28.
*/
'use strict';
const api = global.yoho.API;
const goodsCount = (uid, shoppingKey) => {
let params = {
method: 'app.Shopping.count'
};
if (uid) {
params.uid = uid;
}
if (shoppingKey) {
params.shopping_key = shoppingKey;
}
return api.get('', params);
};
module.exports = {
goodsCount
};
\ No newline at end of file
... ...
/**
* Created by TaoHuang on 2016/9/28.
*/
'use strict';
const api = require('./cart-api');
const _ = require('lodash');
const goodsCount = (uid, shoppingKey) => {
return api.goodsCount(uid, shoppingKey).then(result => _.get(result, 'data.cart_goods_count', 0));
};
module.exports = {
goodsCount
};
... ...
... ... @@ -11,189 +11,230 @@ const mRoot = '../models';
const service = require(`${mRoot}/detail-service`);
const detailHelper = require(`${mRoot}/detail-helper`);
const Actions = require('./lib/actions');
const YohoAction = require('./lib/yoho-action');
const moment = require('moment');
const DEFAULT_AVATAR_ICO = 'http://static.yohobuy.com/images/v3/boy.jpg';
const SEO_SLOGAN = 'YOHO!BUY 有货';
const _ = require('lodash');
class DetailAction extends YohoAction {
/**
* 渲染商品详情
*/
render() {
const req = this.request;
let pid = req.params[0];
let gid = req.params[1];
let saveCurrentGoodsInCookies = _.partial(service.saveRecentGoodInCookies,
req.cookies._browse,
req.cookies._browseskn,
this.response
);
let vipLevel = detailHelper.vipLevel(req.user.vip);
let uid = req.user.uid || 0;
let isStudent = req.user.isStudent || 0;
return service.showMainAsync({
pid: pid,
gid: gid,
channel: this.getSessionChannel(),
gender: this.guessUserGender(),
uid: uid,
isStudent: isStudent,
vipLevel: vipLevel,
saveInCookies: saveCurrentGoodsInCookies
}).then(result=> {
const seo = result.seo;
this.setTitle(seo.title, true, '|');
this.setKeywords(seo.keywords);
this.setDescription(seo.description, true);
this.setEntry('product', 'detail');
this.renderTemplate('product/detail', result);
}).catch(err => {
if (err.code === 404) {
return this.next();
}
const CHANNEL = {
boys: 'boys',
girls: 'girls',
kids: 'kids'
};
return this.next(err);
const _getChannel = (req, res) => {
let channel = req.cookies._Channel;
if (!channel) {
res.cookie('_Channel', CHANNEL.boys, {
domain: '.yohobuy.com',
maxAge: moment.duration(300, 'days').seconds()
});
channel = 'boys'; // 设置默认值
}
return channel;
};
const _getGender = (channel) => {
switch (channel) {
case 'boys':
return '1,3';
case 'girls':
return '2,3';
default:
return '1,2,3';
}
};
/**
* 首屏渲染商品详情
*/
const showMain = (req, res, next) => {
let pid = req.params[0];
let channel = _getChannel(req, res);
let gender = _getGender(channel);
const nullUserInfo = {
uid: null,
isStudent: null,
vipLevel: null,
saveInCookies: null
};
return service.showMainAsync(Object.assign({
pid: pid,
channel: channel,
gender: gender
}, nullUserInfo)).then((result)=> {
return res.render('product/detail', Object.assign({
module: 'product',
page: 'detail',
title_more: true,
title: _.get(result, 'seo.title', '') + ' | ' + SEO_SLOGAN,
keywords: _.get(result, 'seo.keywords', '').replace(/~+/, ''),
description_more: true,
description: result.description
}, result));
}).catch(next);
};
/**
* 商品价格相关的数据
*/
const detailHeader = (req, res, next) => {
let pid = req.query.productId || 0;
let uid = req.user.uid || 0;
let vipLevel = detailHelper.vipLevel(req.user.vip);
let dataMd5 = req.query.md5 || 0;
let saveCurrentGoodsInCookies = _.partial(service.saveRecentGoodInCookies,
req.cookies._browseskn,
res
);
return service.getDetailHeader(pid, uid, req.user.isStudent, vipLevel, dataMd5, saveCurrentGoodsInCookies)
.then((result) => {
if (result.code === 200) {
return res.render('product/detail-header', Object.assign({layout: false}, result.data));
} else {
return res.status(204).end();
}
}).catch(next);
};
/**
* 特殊商品退换货
*/
const detailReturn = (req, res, next) => {
let skn = req.query.skn || 0;
if (!skn) {
return {
code: 400,
message: '商品数据出错'
};
}
}
return service.saleReturn(skn).then(result => {
return res.json({
code: 200,
data: {
result: result
}
});
}).catch(next);
};
/**
* 获取热区图
*/
class HotAreaAction extends YohoAction {
render() {
let pid = this.request.query.productId || 0;
return service.indexHotAreaAsync(pid).then(result => {
this.renderTemplate('product/hotarea', {
hotArea: result,
layout: false
});
const indexHotArea = (req, res, next) => {
let pid = req.query.productId || 0;
return service.indexHotAreaAsync(pid).then((result) => {
res.render('product/hotarea', {
hotArea: result,
layout: false
});
}
}
}).catch(next);
};
/**
* 获得评论列表
*/
class CommentAction extends YohoAction {
render() {
const req = this.request;
let pid = req.query.productId || 0;
let page = req.query.page || 1;
let size = req.query.size || 10;
return service.getShareOrderListAsync(pid, page, size).then((result) => {
let pageResponse = _.get(result, 'data.pageResponse', {});
this.response.json({
code: result.code,
data: _.get(pageResponse, 'list', []).map((item)=> {
return {
avatar: _.get(item, 'userInfo.headIco', '') ?
helpers.image(item.userInfo.headIco, 30, 30) :
DEFAULT_AVATAR_ICO,
userName: _.get(item, 'userInfo.nickName', ''),
date: moment(item.createTime, 'X').format('YYYY-MM-DD HH:mm:ss'),
color: _.get(item, 'goods.color_name', ''),
size: _.get(item, 'goods.size_name', ''),
comment: item.content,
total: pageResponse.totalCount
};
})
});
const indexComment = (req, res, next) => {
let pid = req.query.productId || 0;
let page = req.query.page || 1;
let size = req.query.size || 10;
return service.getShareOrderListAsync(pid, page, size).then((result) => {
let pageResponse = _.get(result, 'data.pageResponse', {});
return res.json({
code: result.code,
data: _.get(pageResponse, 'list', []).map((item)=> {
return {
avatar: _.get(item, 'userInfo.headIco', '') ?
helpers.image(item.userInfo.headIco, 30, 30) :
DEFAULT_AVATAR_ICO,
userName: _.get(item, 'userInfo.nickName', ''),
date: moment(item.createTime, 'X').format('YYYY-MM-DD HH:mm:ss'),
color: _.get(item, 'goods.color_name', ''),
size: _.get(item, 'goods.size_name', ''),
comment: item.content,
total: pageResponse.totalCount
};
})
});
}
}
}).catch(next);
};
/**
* 获得咨询列表
*/
class ConsultAction extends YohoAction {
render() {
const req = this.request;
let uid = req.user.uid || '';
let pid = req.query.productId || 0;
let page = req.query.page || 1;
let size = req.query.size || 10;
return service.indexConsultAsync(uid, pid, page, size).then(result => {
this.response.json({
code: 200,
data: result
});
const indexConsult = (req, res, next) => {
let uid = req.user.uid || '';
let pid = req.query.productId || 0;
let page = req.query.page || 1;
let size = req.query.size || 10;
return service.indexConsultAsync(uid, pid, page, size).then(result => {
return res.json({
code: 200,
data: result
});
}
}
}).catch(next);
};
/**
* 新建咨询
*/
class CreateConsultAction extends YohoAction {
render() {
const req = this.request, res = this.response;
let uid = req.user.uid || '';
let pid = req.body.productId || 0;
let content = req.body.content;
if (content && uid) {
return service.createConsultAsync(uid, pid, content).then(result => {
res.json(result);
});
} else if (!content) {
res.json({
code: 400,
message: '请输入咨询内容'
});
} else if (!uid) {
res.json({
code: 403,
message: '用户没有登录',
data: {
url: helpers.urlFormat('/signin.html')
}
});
} else {
res.json({
code: 400,
message: '请输入咨询内容'
});
}
}
}
const createConsult = (req, res, next) => {
let uid = req.user.uid || '';
let pid = req.body.productId || 0;
let content = req.body.content;
if (content && uid) {
return service.createConsultAsync(uid, pid, content).then(result => {
res.json(result);
}).catch(next);
} else if (!content) {
res.json({
code: 400,
message: '请输入咨询内容'
});
const detailHeader = (req, res, next) => {
let pid = req.query.productId || 0;
let uid = req.user.uid || 0;
let vipLevel = detailHelper.vipLevel(req.user.vip);
let dataMd5 = req.query.md5 || 0;
} else if (!uid) {
res.json({
code: 403,
message: '用户没有登录',
data: {
url: helpers.urlFormat('/signin.html')
}
});
service.getDetailHeader(pid, uid, req.user.isStudent, vipLevel, dataMd5).then((result) => {
if (result.code === 200) {
return res.render('product/detail-header', Object.assign({layout: false}, result.data));
} else {
return res.status(204).end();
}
}).catch(next);
} else {
res.json({
code: 400,
message: '请输入咨询内容'
});
}
};
module.exports = {
showMain: Actions.createAction(DetailAction),
indexHotArea: Actions.createAction(HotAreaAction),
indexComment: Actions.createAction(CommentAction),
indexConsult: Actions.createAction(ConsultAction),
createConsult: Actions.createAction(CreateConsultAction),
productHeader: detailHeader
module.exports = {
showMain,
indexHotArea,
indexComment,
indexConsult,
createConsult,
productHeader: detailHeader,
detailReturn
};
... ...
... ... @@ -17,10 +17,10 @@ const changeFavoriteBrand = (req, res, next) => {
if (uid && brandId) {
brandService.changeAsync(uid, brandId).then(result => {
res.json(result);
return res.json(result);
}).catch(next);
} else if (!uid) {
res.json({
return res.json({
code: 403,
message: '用户ID不存在',
data: {
... ... @@ -28,13 +28,29 @@ const changeFavoriteBrand = (req, res, next) => {
}
});
} else {
res.json({
return res.json({
code: 400,
message: '操作失败'
});
}
};
const isFavoriteBrand = (req, res, next) => {
let uid = req.user.uid || '';
let brandId = req.query.brandId;
if (uid && brandId) {
brandService.isFavoriteAsync(uid, brandId).then(result => {
return res.json(result);
}).catch(next);
} else {
return res.json({
code: 400,
message: '状态失败'
});
}
};
const collectProduct = (req, res, next) => {
let uid = req.user.uid || '';
let pid = req.body.productId;
... ... @@ -43,8 +59,7 @@ const collectProduct = (req, res, next) => {
if (uid && pid) {
switch (type) {
case 'add':
{
productService.createAsync(uid, pid)
productService.createAsync(uid, pid)
.then(result => {
if (result.code === 413) {
result.message = '该商品已经收藏';
... ... @@ -53,22 +68,17 @@ const collectProduct = (req, res, next) => {
res.json(result);
})
.catch(next);
break;
}
break;
case 'cancel':
{
productService.deleteAsync(uid, pid)
productService.deleteAsync(uid, pid)
.then(result => res.json(result))
.catch(next);
break;
}
break;
default:
{
res.json({
code: 400,
message: '错误类型'
});
}
res.json({
code: 400,
message: '错误类型'
});
}
} else if (!uid) {
res.json({
... ... @@ -117,5 +127,6 @@ const collectShop = (req, res, next) => {
module.exports = {
changeFavoriteBrand,
collectProduct,
collectShop
collectShop,
isFavoriteBrand
};
... ...
/**
* AbstractAction
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/7/11
*/
'use strict';
const _ = require('lodash');
const Promise = require('bluebird');
class AbstractAction {
constructor(req, res, next) {
if (!req || !res) {
throw new Error('Request and response object must be specified.');
}
this.request = req;
this.response = res;
this.next = next;
this.renderContext = {};
}
/**
* 判断是否是AJAX请求
*
* @return boolean 如果是AJAX请求返回 true
*/
/**
* 设置入口
*
* @param module 模块名称
* @param entry 入口名称
*/
setEntry(module, entry) {
_.merge(this.renderContext, {
module: module,
page: entry
});
}
/**
* 渲染视图
*
* @param template 模版名称
* @param context 上下文
*/
renderTemplate(template, context) {
this.response.render(template, _.merge({}, this.renderContext, context));
}
/**
* 内部渲染方法,该方法应该由 ActionCreator 来调用
*
* @returns Promise
*/
_render() {
return this.render();
}
/**
* 渲染回调方法
*
* @returns {Promise}
*/
render() {
return new Promise(function(resolve) {
return resolve();
});
}
}
module.exports = AbstractAction;
/**
* Actions
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/7/11
*/
'use strict';
const AbstractAction = require('./abstract-action');
/**
* 创建 Action
* @param Action
* @returns {Function}
*/
const createAction = Action => {
return ((req, res, next) => {
const ret = new Action(req, res, next)._render();
if (ret && typeof Promise.catch === 'function') {
ret.catch(next);
}
});
};
module.exports = {
createAction,
AbstractAction
};
/**
* YohoAction
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/7/11
*/
'use strict';
const CHANNEL_BOYS = 'boys';
const CHANNEL_GIRLS = 'girls';
const CHANNEL_KIDS = 'kids';
const CHANNEL_DEFAULT = CHANNEL_BOYS;
module.exports = {
CHANNEL_DEFAULT,
CHANNEL_BOYS,
CHANNEL_GIRLS,
CHANNEL_KIDS
};
/**
* YohoAction
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/7/11
*/
'use strict';
const AbstractAction = require('./abstract-action');
const Channel = require('./channel');
const moment = require('moment');
const COOKIE_DOMAIN = '.yohobuy.com';
const COOKIE_CHANEL_MAX_AGE = 300;
const _ = require('lodash');
class YohoAction extends AbstractAction {
/**
* 设置网站SEO的标题
*
* @param title 标题
* @param sign 连接的字符串
* @param showMore 是否显示更多内容
* @return void
*/
setTitle(title, showMore, sign) {
showMore = showMore || true;
sign = sign || ' | ';
_.merge(this.renderContext, {
title_more: showMore,
title: title + sign
});
}
getUid() {
return this.request.user.uid;
}
/**
* 获得当前用户所在的频道
*
* @returns {*}
*/
getSessionChannel() {
const channel = this.request.cookies._Channel || Channel.CHANNEL_DEFAULT;
if (!channel) {
// 设置默认频道
this.setSessionChannel(Channel.CHANNEL_BOYS);
}
return channel;
}
/**
* 设置当前用户的频道
*/
setSessionChannel(channel) {
this.response.cookie('_Channel', channel || Channel.CHANNEL_DEFAULT, {
domain: COOKIE_DOMAIN,
maxAge: moment.duration(COOKIE_CHANEL_MAX_AGE, 'days').seconds()
});
}
/**
* 根据用户访问的频道猜测用户性别
*/
guessUserGender() {
switch (this.getSessionChannel()) {
case 'boys':
return '1,3';
case 'girls':
return '2,3';
default:
return '1,2,3';
}
}
/**
* 设置网站SEO的关键词
*
* @param keywords 关键词,多个之间用","逗号分隔
*/
setKeywords(keywords) {
// this->_view->assign('keywords', rtrim(keywords, ',') . ',');
_.merge(this.renderContext, {
keywords: keywords.replace(/~+/, '')
});
}
/**
* 设置网站SEO的描述内容
*
* @param description 描述内容
* @param showMore 是否显示更多内容
* @param sign 连接的字符串
*/
setDescription(description, showMore, sign) {
_.merge(this.renderContext, {
description_more: showMore,
description: description + sign
});
}
}
module.exports = YohoAction;
... ... @@ -6,6 +6,7 @@
const Promise = require('bluebird');
const co = Promise.coroutine;
const api = require('./brand-api');
const _ = require('lodash');
const getBrandByDomainAsync = domain => {
return co(function*() {
... ... @@ -24,7 +25,7 @@ const getBrandByDomainAsync = domain => {
result.brandNameCn = brandInfo.data.brand_name_cn || '';
result.brandAbout = brandInfo.data.brand_intro || '';
result.shopTemplateType = brandInfo.data.shop_template_type ?
parseInt(brandInfo.data.shop_template_type) : '';
_.parseInt(brandInfo.data.shop_template_type) : '';
result.type = brandInfo.data.type ? +brandInfo.data.type : 0;
result.shopId = brandInfo.data.shop_id || '';
... ...
... ... @@ -86,6 +86,23 @@ const getPromotionAsync = (skn) => {
return api.get('', params);
};
const getLimitedProductStatusAsync = (code, uid, skn) => {
let params = {
method: 'app.limitProduct.productStatus',
limitProductCode: code
};
if (uid) {
params.uid = uid;
}
if (skn) {
params.product_skn = skn;
}
return api.get('', params);
};
module.exports = {
getProductBannerAsync,
sizeInfoAsync,
... ... @@ -94,5 +111,6 @@ module.exports = {
getProductModelTryAsync,
getProductAsync,
getPromotionAsync,
isSupportReturnedSale
isSupportReturnedSale,
getLimitedProductStatusAsync
};
... ...
... ... @@ -35,16 +35,11 @@ const EXHIBITION_TICKET = 51335912;
const _getProductAdditionInfoAsync = (data) => {
return co(function * () {
let productId = _.get(data, 'product_id', 0);
let productSkn = _.get(data, 'product_skn', 0);
let brandId = _.get(data, 'brand_info.brand_id', 0);
// 获取相关数据
let promiseData = {
productBanner: productAPI.getProductBannerAsync(productId),
sizeInfo: productAPI.sizeInfoAsync(productSkn),
productComfort: productAPI.getProductComfortAsync(productId),
productModelCard: productAPI.getProductModelCardAsync(productId),
productModelTry: productAPI.getProductModelTryAsync(productSkn),
bannerInfo: brandService.getBannerInfoAsync(brandId)
};
... ... @@ -54,12 +49,25 @@ const _getProductAdditionInfoAsync = (data) => {
})();
};
const _getProductIntroAsync = (productId, productSkn) => {
return co(function * () {
let result = yield Promise.props({
sizeInfo: productAPI.sizeInfoAsync(productSkn),
productComfort: productAPI.getProductComfortAsync(productId),
productModelCard: productAPI.getProductModelCardAsync(productId),
productModelTry: productAPI.getProductModelTryAsync(productSkn)
});
return result;
})();
};
/**
* 获取商品的喜欢
* pid : product id
* bid : brand id
*/
const _getProductFavoriteDataAsync = (uid, pid, bid) => {
const _getProductFavoriteDataAsync = (uid, pid) => {
return co(function*() {
let result = {
product: false,
... ... @@ -70,16 +78,18 @@ const _getProductFavoriteDataAsync = (uid, pid, bid) => {
return result;
}
if (pid) {
let productData = yield favoriteProductService.isFavoriteAsync(uid, pid);
let requestApi = {};
result.product = productData.code === 200 && productData.data ? true : false;
if (pid) {
requestApi.product = favoriteProductService.isFavoriteAsync(uid, pid);
}
if (bid) {
let brandData = yield favoriteBrandService.isFavoriteAsync(uid, bid);
let requestData = yield Promise.props(requestApi);
let productData = requestData.product;
result.brand = brandData.code && brandData.code === 200 ? true : false;
if (productData) {
result.product = productData.code === 200 && productData.data ? true : false;
}
return result;
... ... @@ -162,7 +172,7 @@ const _getVipDataByProductBaseInfo = (data, vipLevel, uid) => {
const _getProductActivityBanner = (additionalData) => {
let data = additionalData.productBanner;
if (_.isElement(data) || _.get(data, 'code', 400) !== 200 || !_.get(data, 'data.bannerImg')) {
if (_.isEmpty(data) || _.get(data, 'code', 400) !== 200 || !_.get(data, 'data.bannerImg')) {
return {};
}
... ... @@ -191,20 +201,10 @@ const _getActivityDataByProductBaseInfo = (data, additionalData) => {
};
/**
* 获取商品咨询和评论数据
* @param data
*/
const _getConsultCommentDataByProductInfo = () => {
return {
commentUrl: helpers.urlFormat('/home/comment')
};
};
/**
* 获取品牌数据
*/
const _getBrandDataByProductBaseInfo = (data, additionalData) => {
if (!data.brand_info) {
if (!_.get(data, 'brand_info')) {
return {};
}
... ... @@ -212,7 +212,7 @@ const _getBrandDataByProductBaseInfo = (data, additionalData) => {
let bgImg = '';
let logo = '';
let bannerInfo = null;
let result = additionalData.bannerInfo;
let result = _.get(additionalData, 'bannerInfo', {});
if (_.isEmpty(result)) {
return {};
... ... @@ -236,7 +236,7 @@ const _getBrandDataByProductBaseInfo = (data, additionalData) => {
let homeUrl = 'javascript:void(0)'; // eslint-disable-line no-script-url
if (data.brand_info.brand_domain) {
if (_.get(data, 'brand_info.brand_domain')) {
homeUrl = helpers.urlFormat('', null, data.brand_info.brand_domain);
}
... ... @@ -262,7 +262,7 @@ const _getSkuDataByProductBaseInfo = (data) => {
let defaultImage = '';// 默认图
let chooseSkuFlag = false; // 选中状态
if (!data.goods_list) {
if (_.isEmpty(_.get(data, 'goods_list', []))) {
return {
totalStorageNum,
skuGoods,
... ... @@ -475,25 +475,17 @@ function _getSortNavAsync(smallSortId, gender) {
}
// 保存在 gids 和 skns ,最近流览功能
const saveRecentGoodInCookies = (oldGids, oldSkns, res, addGids, addSkns) => {
oldGids = (oldGids || '').split(',');
oldSkns = (oldSkns || '').split(',');
addSkns = `${addSkns}-${addGids}`;
const saveRecentGoodInCookies = (oldSkns, res, addSkns) => {
_.remove(oldGids, addGids);
_.remove(oldSkns, addSkns);
oldSkns = oldSkns ? oldSkns.split(',') : [];
oldGids.unshift(addGids);
oldSkns.unshift(addSkns);
oldSkns = _.reject(oldSkns, old => old === String(addSkns) ? true : false);
res.cookie('_browse', oldGids.splice(0, 30).join(','), {
maxAge: 2000000000,
domain: 'yohobuy.com'
});
oldSkns.unshift(addSkns);
res.cookie('_browseskn', oldSkns.splice(0, 30).join(','), {
maxAge: 2000000000,
domain: 'yohobuy.com'
domain: '.yohobuy.com'
});
};
... ... @@ -948,7 +940,7 @@ const _getSizeAttrByMaxSortId = (maxSortId, sizeList) => {
* @param maxSortId
* @return object
*/
const _getSizeInfo = (productSkn, maxSortId, additionalData)=> {
const _getIntroInfo = (productSkn, maxSortId, additionalData)=> {
if (!productSkn) {
return {};
}
... ... @@ -1004,17 +996,15 @@ const _getSizeInfo = (productSkn, maxSortId, additionalData)=> {
* @param array $navs
* @return array
*/
const _getSeoByGoodsInfo = function(goodsInfo, navs) {
const _getSeoByGoodsInfo = (goodsInfo, navs) => {
let title = '';
let keywords = '';
let brandName = '';
let sortName = '';
let description = '';
goodsInfo = goodsInfo || {};
navs = navs || [];
if (!_.isEmpty(goodsInfo.brandName)) {
if (goodsInfo.brandName) {
title = goodsInfo.brandName + ' ';
brandName = goodsInfo.brandName;
}
... ... @@ -1025,9 +1015,9 @@ const _getSeoByGoodsInfo = function(goodsInfo, navs) {
}
title += goodsInfo.name + '正品 ';
keywords = brandName + sortName + ',' + brandName + '官网专卖店,' + brandName + '官方授权店,' +
let keywords = brandName + sortName + ',' + brandName + '官网专卖店,' + brandName + '官方授权店,' +
brandName + '正品,' + brandName + '打折,' + brandName + '折扣店,' + brandName + '真品,' + brandName + '代购';
description = !goodsInfo.shareDesc ? goodsInfo.name : goodsInfo.shareDesc;
let description = !goodsInfo.shareDesc ? goodsInfo.name : goodsInfo.shareDesc;
return {
title: title,
... ... @@ -1041,12 +1031,22 @@ const _getSeoByGoodsInfo = function(goodsInfo, navs) {
* @param origin Object 原始数据
* @return result Object 格式化数据
*/
const _detailDataPkg = (origin, uid, vipLevel) => {
const _detailDataPkg = (origin, uid, vipLevel, cookies) => {
return co(function*() {
let result = {}; // 结果输出
let md5 = origin.md5;
if (_.isEmpty(origin) || _.isEmpty(origin.data)) {
return {};
}
let result = {};
result.md5 = origin.md5;// 用于前端数据变化的对比
origin = origin.data;
if (uid) {
origin.uid = uid;
}
let propOrigin = _.partial(_.get, origin);
// 商品名称
... ... @@ -1054,34 +1054,51 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
return result;
}
origin.uid = uid;
result.name = propOrigin('product_name');
result.skn = propOrigin('product_skn');
result.productId = propOrigin('product_id');
result.md5 = md5; // 用于前端数据变化的对比
result.maxSortId = propOrigin('maxSortId', '');
result.smallSortId = propOrigin('smallSortId', '');
result.goCartUrl = helpers.urlFormat('/shopping/cart');
let brandId = propOrigin('brand_info.brand_id', 0);
let requestData = yield Promise.all([
_getProductAdditionInfoAsync(origin), // 预处理所有的数据
_getProductFavoriteDataAsync(uid, result.productId, brandId), // 处理收藏喜欢数据
productAPI.getPromotionAsync(result.skn)
]);
let requestApi = {
addition: _getProductAdditionInfoAsync(origin), // 预处理所有的数据
fav: _getProductFavoriteDataAsync(uid, result.productId), // 处理收藏喜欢数据
promotion: productAPI.getPromotionAsync(result.skn) // 打折信息
};
if (propOrigin('isLimitBuy', false) && propOrigin('limitProductCode', '')) {
requestApi.limited = productAPI.getLimitedProductStatusAsync(
propOrigin('limitProductCode'),
uid,
result.skn
); // 限购商品的状态
}
if (propOrigin('brand_info', '')) {
requestApi.brand =
brandService.getBrandByDomainAsync(propOrigin('brand_info.brand_domain')); // 品牌信息
}
let requestData = yield Promise.props(requestApi);
let additionalData = requestData[0];
let favoriteData = requestData[1];
let promotionData = requestData[2];
let additionalData = requestData.addition;
let favoriteData = requestData.fav;
let promotionData = requestData.promotion;
let limitedInfo = requestData.limited;
let domainBrand = requestData.brand;
// 商品标签
result.tags = _getTagsDataByProductInfo(origin);
// 商品促销短语
// 商品促销短语
result.saleTip = propOrigin('sales_phrase', '');
// 是否收藏
result.isCollect = favoriteData.product;
// 商品价格
result.marketPrice = propOrigin('format_market_price');
result.salePrice = propOrigin('format_sales_price');
... ... @@ -1127,20 +1144,6 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
result.hasOtherPrice = false;
}
// 商品咨询和评论数据,当前为空
let consultComment = _getConsultCommentDataByProductInfo();
// 品牌信息
let banner = {};
if (propOrigin('brand_info', '')) {
result.brandImg = helpers.image(propOrigin('brand_info.brand_ico', ''), 47, 47);
result.brandName = propOrigin('brand_info.brand_name', '');
result.brandUrl = helpers.urlFormat('', null, propOrigin('brand_info.brand_domain'));
banner = _getBrandDataByProductBaseInfo(origin, additionalData);
banner.isCollect = favoriteData.brand;
}
// sku商品信息
let skuData = _getSkuDataByProductBaseInfo(origin);
... ... @@ -1148,15 +1151,13 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
result.colors = skuData.skuGoods;
let totalStorageNum = skuData.totalStorageNum;
// 是否收藏
result.isCollect = favoriteData.product;
if (propOrigin('isLimitBuy', false)) {
// 限购商品
if (limitedInfo && limitedInfo.code === 200 && _.get(limitedInfo, 'data.isLimitBuy', false) === true) {
// 是否开售
let isBeginSale = propOrigin('saleStatus', 0) === 1;
let isBeginSale = _.get(limitedInfo, 'data.saleStatus', 0) === 1;
// 限购商品有关的展示状态
let showStatus = propOrigin('showStatus', 1);
let showStatus = _.get(limitedInfo, 'data.showStatus', 1);
let fashTopGoods = _getFashionTopGoodsStatus(uid, showStatus, isBeginSale);
... ... @@ -1177,6 +1178,7 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
}
}
// 商品购买状态
let soldOut = !!(propOrigin('status') === 0 || totalStorageNum === 0);
let notForSale = propOrigin('attribute') === 2; // 非卖品
let virtualGoods = propOrigin('attribute') === 3; // 虚拟商品
... ... @@ -1244,44 +1246,62 @@ const _detailDataPkg = (origin, uid, vipLevel) => {
statGoodsInfo.smallSortId = result.smallSortId;
statGoodsInfo.soldOut = soldOut ? 1 : 0;
if (banner.brandId) {
let domainBrand = yield brandService.getBrandByDomainAsync(banner.brandDomain);
// 商品的品牌信息
let bandInfo = {};
if (propOrigin('brand_info', '')) {
result.brandImg = helpers.image(propOrigin('brand_info.brand_ico', ''), 47, 47);
result.brandName = propOrigin('brand_info.brand_name', '');
result.brandUrl = helpers.urlFormat('', null, propOrigin('brand_info.brand_domain'));
bandInfo = _getBrandDataByProductBaseInfo(origin, additionalData);
bandInfo.isCollect = favoriteData.brand;
if (domainBrand.type && domainBrand.shopId) {
let type = _.parseInt(domainBrand.type);
if (type === 1) {
// 多品店不显示
banner = {};
bandInfo = {};
} else if (type === 2) {
// 单品店显示新版的店铺banner
let basisData = yield shopService.basisTemplateAsync(domainBrand.shopId);
banner.bgImg = basisData.shopTopBanner.banner || banner.bgImg;
bandInfo.bgImg = basisData.shopTopBanner.banner || bandInfo.bgImg;
}
}
}
// 最近浏览功能 ,限量商品不加入到最近浏览
if (!_.has(result, 'fashionTopGoods')) {
cookies && cookies(_.get(result, 'skn', ''));
}
// 获取商品尺寸相关
let sizeInfo = _getSizeInfo(result, result.maxSortId, additionalData);
return Object.assign({
return {
goodsInfo: result,
consultComment: consultComment,
banner: _.isEmpty(banner) ? null : banner,
banner: _.isEmpty(bandInfo) ? null : bandInfo,
statGoodsInfo: statGoodsInfo
}, sizeInfo);
};
})();
};
const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5) => {
let currentUserProductInfo = _.partial(_detailDataPkg, _, uid, vipLevel);
/**
* 获得商品价格,活动等数据
*/
const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5, cookie) => {
let currentUserProductInfo = _.partial(_detailDataPkg, _, uid, vipLevel, cookie);
return productAPI.getProductAsync(pid, uid, isStudent, vipLevel)
.then(result => currentUserProductInfo(result))
.then(currentUserProductInfo)
.then((result) => {
if (result.goodsInfo.md5 !== dataMd5 || uid) {
if (_.isEmpty(result) || !_.get(result, 'goodsInfo.md5')) {
return {
code: 204, // 没有改变数据
data: {}
};
}
if (_.get(result, 'goodsInfo.md5') !== dataMd5 || uid) {
return {
code: 200, // 改变数据
data: result
... ... @@ -1296,60 +1316,81 @@ const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5) => {
};
/**
* 是否支持退换货,true 支持,false 不支持
*/
const saleReturn = (skn) => {
return productAPI.isSupportReturnedSale(skn).then(result => _.get(result, `data.${skn}`, 'N') === 'N' ? 'Y' : 'N');
};
/**
* 获取某一个商品详情主页面
*/
const showMainAsync = (data) => {
return co(function * () {
let result = {};
let currentUserProductInfo = _.partial(_detailDataPkg, _, data.uid, data.vipLevel);
// 获取商品基本信息
let productData = yield productAPI.getProductAsync(data.pid, data.uid, data.isStudent, data.vipLevel);
// 获取商品信息
let productInfo = yield productAPI.getProductAsync(data.pid, data.uid, data.isStudent, data.vipLevel)
.then(currentUserProductInfo);
if (_.isEmpty(productInfo) || _.isEmpty(productInfo.goodsInfo)) {
if (_.isEmpty(productData.data)) {
return Promise.reject({
code: 404
code: 404,
message: 'app.product.data api wrong'
});
}
let smallSortId = _.get(productData, 'data.smallSortId');
let maxSortId = _.get(productData, 'data.maxSortId');
let productId = _.get(productData, 'data.product_id');
let productSkn = _.get(productData, 'data.product_skn');
let curUserProduct = _.partial(_detailDataPkg, _, data.uid, data.vipLevel, data.gid, data.saveInCookies);
let requestData = yield Promise.all([
_getSortNavAsync(productInfo.goodsInfo.smallSortId, data.gender),
HeaderModel.requestHeaderData(data.channel),
productAPI.isSupportReturnedSale(productInfo.goodsInfo.skn)
_getSortNavAsync(smallSortId, data.gender), // 面包屑导航
HeaderModel.requestHeaderData(data.channel), // 通用头部数据
_getProductIntroAsync(productId, productSkn), // 商品详细介绍
curUserProduct(productData) // 商品详细价格
]);
// 分类导航 ,seo
let navs = requestData[0];
let seo = _getSeoByGoodsInfo(productInfo.goodsInfo, navs);
result.seo = seo;
// 最近浏览功能 ,限量商品不能使用这个功能
if (!_.has(productInfo, 'goodsInfo.fashionTopGoods')) {
data.saveInCookies(data.gid, _.get(productInfo, 'goodsInfo.skn', ''));
}
let smallSortNavigator = requestData[0];
let navigatorHeader = requestData[1];
let productDescription = requestData[2];
let productInfo = requestData[3];
// 拼装数据
let result = {};
result.headerData = requestData[1].headerData;
// 商品价格
result.productDetailPage = true;
result.detail = productInfo;
result.statGoodsInfo = Object.assign({fullSortName: navs.map(x => x.name).join('-')},
// 商品介绍
let intro = _getIntroInfo(productSkn, maxSortId, productDescription);
result.deatil = Object.assign(result.detail, intro);
// seo
result.seo = _getSeoByGoodsInfo(productInfo.goodsInfo, smallSortNavigator);
// 商品页面统计
result.statGoodsInfo = Object.assign({fullSortName: smallSortNavigator.map(x => x.name).join('-')},
productInfo.statGoodsInfo
);
// 是否支持退换货,true 支持,false 不支持
result.detail.supportSaleReturnedService =
_.get(requestData, `[2].data.${productInfo.goodsInfo.skn}`, 'N') === 'N';
// 导航
// 面包屑导航
result.detail.pathNav = _.concat(
homeService.getHomeChannelNav(data.channel),
navs,
[{name: productInfo.goodsInfo.name}]
smallSortNavigator,
[{name: _.get(productInfo, 'goodsInfo.name')}]
);
// 头部数据
result.headerData = navigatorHeader.headerData;
// 咨询和评论
result.detail.consultComment = true;
// 最近浏览,最多5条记录
result.detail.latestWalk = 5;
return result;
})();
};
... ... @@ -1362,5 +1403,6 @@ module.exports = {
showMainAsync: showMainAsync, // 获取某一个商品详情主页面
indexHotAreaAsync: hotAreaService.indexAsync, // 获取某一个商品的热区数据
saveRecentGoodInCookies, // 保存最近的商品
getDetailHeader
getDetailHeader,
saleReturn
};
... ...
... ... @@ -492,7 +492,7 @@ const getAdnav = (params) => {
if (result[1].code === 200 && result[1].data) {
dest.picTitle = brandFolderSeries;
Object.assign(dest.list, searchHandler.handleFolderData(result[0].data));
Object.assign(dest.list, searchHandler.handleSeriesData(result[1].data));
}
... ... @@ -516,6 +516,7 @@ const getShopInfo = (shopId, uid) => {
isFavorite: result.data.is_favorite === 'Y',
shopTemplateType: result.data.shop_template_type,
multBrandShopType: result.data.mult_brand_shop_type,
shopName: result.data.shop_name,
showShopName: result.data.is_show_shop_name === 'Y'
};
} else {
... ... @@ -549,7 +550,8 @@ const getShopData = (shopId, channel, params, shopInfo) => {
Object.assign(finalResult,
result[0], // 头部数据
searchHandler.handlePathNavData(shopInfo, params, 'shop', channel) // 面包屑导航
searchHandler.handlePathNavData(shopInfo, params, 'shop', channel), // 面包屑导航
shopHandler.setShopSeo(shopInfo.shopName || shopInfo.brandName) // 店铺SEO
);
_.set(finalResult, 'headerData.header', true);
... ... @@ -786,6 +788,9 @@ const getShopListData = (channel, params, uid) => {
isFavorite: data.is_favorite === 'Y',
brandCont: data.shop_intro || ''
});
// 店铺SEO
Object.assign(finalResult, shopHandler.setShopSeo(data.shop_name));
}
} else {
return Promise.reject('No ShopDecorator data');
... ... @@ -915,6 +920,9 @@ const getBaseShopData = (params, extra, channel, shopId) => {
name: shopName,
pathTitle: shopName
});
// 店铺SEO
Object.assign(resData, shopHandler.setShopSeo(shopName));
}
// 临时删除seo信息
... ...
... ... @@ -731,9 +731,9 @@ exports.handlePagerData = (total, params) => {
pages: []
};
let currentPage = parseInt((_.isEmpty(params.page) ? 1 : params.page), 10); // 当前页
let perPageCount = parseInt((_.isEmpty(params.limit) ? 60 : params.limit), 10); // 每页商品数
let totalPage = parseInt(total / perPageCount, 10) + 1; // 总页数
let currentPage = parseInt(_.get(params, 'page', 1), 10); // 当前页
let perPageCount = parseInt(_.get(params, 'limit', 60), 10); // 每页商品数
let totalPage = Math.ceil(total / perPageCount); // 总页数
if (currentPage === 1) {
// 当前页为 1,一定没有上一页
... ... @@ -773,11 +773,14 @@ exports.handlePagerData = (total, params) => {
}
} else if (currentPage > totalPage - 2) {
for (let i = totalPage; i >= totalPage - 4; i--) {
pages.push({
url: handleFilterUrl(params, {page: i}),
num: i,
cur: currentPage === i
});
if (i > 0) {
pages.push({
url: handleFilterUrl(params, {page: i}),
num: i,
cur: currentPage === i
});
}
}
pages = _.sortBy(pages, ['num']);
}
... ... @@ -795,7 +798,7 @@ exports.handlePagerData = (total, params) => {
num: '...'
});
}
if (currentPage < totalPage - 2) {
if (currentPage < totalPage - 2 && totalPage > 5) {
nextPages.push({
num: '...'
});
... ...
/**
* Created by TaoHuang on 2016/6/14.
*/
'use strict';
const _ = require('lodash');
const md5 = require('md5');
const api = global.yoho.SearchAPI;
const yohoApi = global.yoho.API;
const api = global.yoho.API;
const serviceApi = global.yoho.ServiceAPI;
const _ = require('lodash');
const helpers = global.yoho.helpers;
const images = require('../../../utils/images.js');
const cache = global.yoho.cache;
const logger = global.yoho.logger;
const md5 = require('md5');
const config = require('../../../config/common');
const images = require('../../../utils/images.js');
const getSortByConditionAsync = (condition) => {
return api.get('sortgroup.json', condition);
return api.get('', Object.assign({
method: 'web.regular.groupsort'
}, condition));
};
// 判断用户是否收藏品牌
... ... @@ -40,12 +38,7 @@ const getSearchCackeKey = params => {
return md5(ks.join('_'));
};
const getProductListOrig = (finalParams) => {
return yohoApi.get('', finalParams).then(result => {
return result;
});
};
const getProductListOrig = finalParams => api.get('', finalParams);
/**
* 获取商品列表
... ... @@ -104,11 +97,7 @@ const getProductList = (params) => {
}
};
const getSortListOrig = (finalParams) => {
return yohoApi.get('', finalParams).then(ret => {
return ret;
});
};
const getSortListOrig = (finalParams) => api.get('', finalParams);
/**
* 获取分类列表
... ... @@ -164,7 +153,7 @@ const getSortIntro = (params) => {
};
Object.assign(finalParams, params);
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -177,7 +166,7 @@ const getSortAds = (params) => {
};
Object.assign(finalParams, params);
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -192,7 +181,7 @@ const getBrandSeries = (params) => {
status: params.status || 1
};
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -207,7 +196,7 @@ const getBrandFolder = (params) => {
status: params.status || 1
};
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -221,7 +210,7 @@ const getNodeContent = (params) => {
node: params.node || ''
};
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -235,7 +224,7 @@ const getWeekNew = (params) => {
};
Object.assign(finalParams, params);
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -266,7 +255,7 @@ const getBrandShop = (query) => {
if (retObj) {
return retObj;
} else {
return yohoApi.get('', finalParams).then(ret => {
return api.get('', finalParams).then(ret => {
if (ret && ret.code === 200) {
cache.set(cKey, ret.data, 3600);
... ... @@ -304,7 +293,7 @@ const getShopsByBrandId = bid => {
if (cdataObj) {
return cdataObj;
} else {
return yohoApi.get('', finalParams).then(ret => {
return api.get('', finalParams).then(ret => {
if (ret && ret.code === 200) {
cache.set(cKey, ret.data, 3600);
... ... @@ -438,7 +427,7 @@ const getSuggest = (params) => {
keyword: params.keyword || ''
};
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
... ... @@ -452,7 +441,7 @@ const getBrandData = (params) => {
domain: params.domain || ''
};
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
/**
... ... @@ -477,7 +466,7 @@ const getShopInfo = (shopId, uid) => {
uid: uid || 0
};
return yohoApi.get('', finalParams);
return api.get('', finalParams);
};
... ... @@ -485,15 +474,14 @@ const getShopInfo = (shopId, uid) => {
* 查询店铺下面的所有品牌
*/
const getShopBrands = (shopId) => {
return yohoApi.get('', {method: 'app.shops.getShopsBrands', shop_id: shopId || 0});
return api.get('', {method: 'app.shops.getShopsBrands', shop_id: shopId || 0});
};
/**
* 查询店铺装修
*/
const getShopDecorator = (shopId) => {
return yohoApi.get('', {method: 'app.shopsdecorator.getList', shop_id: shopId || 0});
return api.get('', {method: 'app.shopsdecorator.getList', shop_id: shopId || 0});
};
/**
... ... @@ -515,7 +503,7 @@ const getBrands4Filter = (params) => {
method: 'web.regular.aggBrand'
};
return yohoApi.get('', Object.assign({}, params, finalParams));
return api.get('', Object.assign(params, finalParams));
};
module.exports = {
... ...
... ... @@ -1249,7 +1249,7 @@ exports.handleBrandBanner = (data) => {
};
/**
* 处理品牌系列
* 处理品牌系列folder_id
* @type {[type]}
*/
exports.handleFolderData = (data) => {
... ... @@ -1266,6 +1266,23 @@ exports.handleFolderData = (data) => {
};
/**
* 处理品牌系列series
* @type {[type]}
*/
exports.handleSeriesData = (data) => {
let dest = [];
_.forEach(data, (value) => {
dest.push({
href: '?series=' + value.id,
src: value.series_banner
});
});
return dest;
};
/**
* 筛选类链接处理的对外接口
* @type {[type]}
*/
... ...
... ... @@ -16,6 +16,14 @@ const newProductsName = '新品上架 NEW';
const hotProductsName = '人气单品 HOT';
const shopListUrl = '/product/shoplist';
exports.setShopSeo = (shopName) => {
return {
title: `${shopName} | ${shopName} 潮流服装服饰-Yoho!Buy有货`,
keywords: `${shopName},${shopName} 服装服饰,${shopName} 潮流服装服饰`,
description: `${shopName} | Yoho!Buy有货 ${shopName} 潮流服饰官方授权店!100%品牌正品保证,支持货到付款。`
};
};
/**
* 新品上架
*/
... ...
... ... @@ -59,6 +59,9 @@ router.post('/detail/consult', auth, detail.createConsult);// 创建咨询
router.get('/detail/hotarea', detail.indexHotArea);// 商品热区
router.post('/index/favoriteBrand', favorite.changeFavoriteBrand);// 收藏品牌
router.post('/item/togglecollect', favorite.collectProduct); // 收藏商品
router.get('/detail/header', detail.productHeader); // 价格数据重新获取接口
router.get('/detail/return', detail.detailReturn);// 特殊商品退换货
router.get('/index/isfav', favorite.isFavoriteBrand);// 品牌收藏状态
// 搜索
router.get('/search/index', search.index);
... ...
... ... @@ -26,7 +26,7 @@
{{> product/standard-content}}
{{/if}}
{{> common/latest-walk}}
{{> product/latest-walk}}
</div>
</div>
</div>
... ...
... ... @@ -23,7 +23,8 @@
{{> common/path-nav}}
{{# goodsInfo}}
<div class="main clearfix" data-skn="{{skn}}" data-id="{{productId}}">
<div class="main clearfix" data-skn="{{skn}}" data-id="{{productId}}" data-md5="{{md5}}"
data-skn="{{skn}}">
<div class="pull-left imgs clearfix">
<div class="pull-left img">
<div class="tags clearfix">
... ... @@ -540,12 +541,20 @@
<span class="title cur">商品详情 DETAILS</span>
</p>
<div id="details-html" class="details-html">
<div class="lazy-load-object">
<textarea class="datalazyload" style="visibility: hidden;">
<script>
fetchHotArea();
</script>
</textarea>
</div>
{{{details}}}
</div>
</div>
{{# consultComment}}
<div class="consult-comment info-block">
<p class="block-title">
<span class="title">顾客咨询(<em class="consult-num">0</em></span>
<span class="sep">|</span>
... ... @@ -561,7 +570,7 @@
</span>
</p>
<p class="btn-wrap">
<a class="btn" href="{{commentUrl}}" target="_blank">
<a class="btn" href="//www.yohobuy.com/home/comment" target="_blank">
<i class="iconfont">&#xe61e;</i>
我要评论
</a>
... ... @@ -599,6 +608,16 @@
</p>
</div>
</div>
<div class="lazy-load-object">
<textarea class="datalazyload" style="visibility: hidden;">
<script>
fetchComment();
fetchReturn();
</script>
</textarea>
</div>
</div>
{{/ consultComment}}
... ... @@ -635,11 +654,8 @@
</div>
</div>
{{#if supportSaleReturnedService}}
<div class="support-saleReturned-service"></div>
{{^}}
<div class="not-support-saleReturned-service"></div>
{{/if}}
<div id="saleReturn" class="support-saleReturned-service"></div>
<div class="service"></div>
{{#if latestWalk}}
... ... @@ -649,7 +665,7 @@
<span class="title cur">最近浏览 RECENT REVIEW</span>
</p>
<div id="latest-walk-goods" class="goods clearfix"></div>
{{> product/latest-walk}}
{{> product/latest-walk-tpl}}
</div>
{{/if}}
</div>
... ... @@ -724,7 +740,8 @@
(function() {
var mvl = document.createElement('script');
mvl.type = 'text/javascript'; mvl.async = true;
mvl.type = 'text/javascript';
mvl.async = true;
mvl.src = ('https:' == document.location.protocol ? 'https://static-ssl.mediav.com/mvl.js' : 'http://static.mediav.com/mvl.js');
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(mvl, s);
... ... @@ -732,13 +749,3 @@
</script>
{{/statGoodsInfo}}
<script type="text/javascript">
(function() {
try {
var timestamp = (new Date()).valueOf();
var view = document.createElement('img');
view.src = 'http://shopping.yohobuy.com/1.jpg?t=' + timestamp;
} catch (e) {
}
})();
</script>
... ...
... ... @@ -18,7 +18,7 @@
<a name="stu-rights">
<h2 class="floor-title">学生权益</h2>
</a>
<span id="rights-dia" class="floor-more">更多细则 ></span>
<span id="rights-dia" class="floor-more">全部权益 ></span>
</div>
<div class="commodity-list clearfix">
{{> students/stu-rights}}
... ...
{{#if latestWalk}}
<input id="latest-walk-count" type="hidden" value="{{latestWalk}}">
<div class="latest-walk">
<h2>最近浏览的商品</h2>
<div id="latest-walk-goods" class="goods clearfix"></div>
</div>
{{> product/latest-walk-tpl}}
{{/if}}
\ No newline at end of file
<script id="latest-walk-tpl" type="text/html">
\{{# latestWalk}}
<div class="good">
<a href="\{{href}}" target="_blank">
<img class="lazy" data-original="\{{img}}">
</a>
<a class="name" href="\{{href}}" target="_blank">\{{name}}</a>
<p class="price">
<span class="market-price">\{{marketPrice}}</span>
<span class="sale-price">\{{salePrice}}</span>
</p>
</div>
\{{/ latestWalk}}
</script>
\ No newline at end of file
<div class="stu-rights clearfix">
<div id="stuRights" class="stu-rights clearfix">
{{#rightsItem}}
<div class="stu-rights-item">
<img class="" src="{{image src 300 300}}">
</img>
</div>
{{/rightsItem}}
</div>
\ No newline at end of file
</div>
... ...
... ... @@ -36,8 +36,7 @@ module.exports = {
master: ['127.0.0.1:11211'],
slave: ['127.0.0.1:11211'],
session: ['127.0.0.1:11211'],
reconnect: 5000,
timeout: 100,
timeout: 1000,
retries: 0
},
interfaceShunt: {
... ... @@ -116,9 +115,8 @@ if (isProduction) {
master: ['127.0.0.1:12111'],
slave: ['127.0.0.1:12112'],
session: ['127.0.0.1:12111'],
timeout: 100,
retries: 0,
reconnect: 5000
timeout: 1000,
retries: 0
}
});
}
... ...
... ... @@ -7,6 +7,7 @@
module.exports = app => {
// 公共服务
app.use('/common', require('./apps/common'));
// 业务模块
app.use(require('./apps/channel')); // 频道页
... ... @@ -14,5 +15,7 @@ module.exports = app => {
app.use('/product', require('./apps/product')); // 商品相关页面
app.use(require('./apps/passport')); // 登录注册
app.use('/home', require('./apps/home')); // 会员中心
app.use('/brands', require('./apps/brands'));
app.use('/guang', require('./apps/guang'));
app.use('/cart', require('./apps/cart'));// 购物车
};
... ...
... ... @@ -52,10 +52,13 @@ const getNavBar = (data, type) => {
let obj = {};
let lowEn = _.camelCase(item.sort_name_en).toLowerCase();
obj.link = item.sort_url;
obj.cn = item.sort_name;
obj.en = item.sort_name_en;
obj.isNewPage = item.is_new_page === 'Y' ? true : false;
Object.assign(obj, {
type: lowEn,
link: item.sort_url,
cn: item.sort_name,
en: item.sort_name_en,
isNewPage: item.is_new_page === 'Y'
});
if (type === lowEn) {
obj.active = true;
... ... @@ -135,30 +138,38 @@ const getThirdNav = (data) => {
* @param {String} type 频道类型
* @return {array} 子菜单数组
*/
const getSubNav = (data, type) => {
let subNav = [];
const getSubNavGroup = (data, type) => {
let subNavGroup = [];
_.forEach(data, it => {
if (type === _.camelCase(it.sort_name_en).toLowerCase()) {
_.forEach(it.sub, item => {
let obj = {};
obj.link = item.sort_url;
obj.name = item.sort_name;
obj.isHot = item.is_hot === 'Y' ? true : false;
obj.isNew = item.is_new === 'Y' ? true : false;
if (item.sub) {
obj.thirdNav = getThirdNav(item.sub);
obj.imgCode = item.content_code;
}
subNav.push(obj);
});
}
let subNav = [];
_.forEach(it.sub, item => {
let obj = {};
obj.link = item.sort_url;
obj.name = item.sort_name;
obj.isHot = item.is_hot === 'Y' ? true : false;
obj.isNew = item.is_new === 'Y' ? true : false;
if (item.sub) {
obj.thirdNav = getThirdNav(item.sub);
obj.imgCode = item.content_code;
}
subNav.push(obj);
});
let lowEn = _.camelCase(it.sort_name_en).toLowerCase();
subNavGroup.push({
subType: lowEn,
subNav: subNav,
active: lowEn === type
});
});
return subNav;
return subNavGroup;
};
... ... @@ -174,7 +185,7 @@ const setHeaderData = (resData, type) => (
headType: type,
yohoGroup: getMenuData(),
navbars: resData ? getNavBar(resData, type) : [],
subNav: resData ? getSubNav(resData, type) : []
subNavGroup: resData ? getSubNavGroup(resData, type) : []
}
);
... ...
{{# headerData}}
<div class="yoho-header {{headType}}">
<div id="yoho-header" class="yoho-header" data-type="{{headType}}">
<div class="tool-wrapper clearfix">
<div class="center-content">
<div class="yoho-group-map left">
... ... @@ -19,8 +19,8 @@
<ul>
<li id="loginBox">
<span class="hi">Hi~</span>
[ <a href="//www.yohobuy.com/signin.html" class="loginbar">请登录</a> ]
[ <a href="//www.yohobuy.com/reg.html" class="registbar">免费注册</a> ]
[ <a id="signin-url" href="//www.yohobuy.com/signin.html" class="loginbar">请登录</a> ]
[ <a id="reg-url" href="//www.yohobuy.com/reg.html" class="registbar">免费注册</a> ]
</li>
<li class="myyoho" id="myYohoBox">
<span class="tag-seprate"></span>
... ... @@ -66,7 +66,7 @@
<div class="main-logo"><a href="//www.yohobuy.com/" class="main-link"></a></div>
<ul class="main-nav-list">
{{# navbars}}
<li {{#if active}} class="cure"{{/if}}{{#if ico}} style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}>
<li class="{{type}}"{{#if ico}} style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}>
{{#if ico}}
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"></a>
{{^}}
... ... @@ -80,7 +80,7 @@
</li>
{{/ navbars}}
</ul>
<div class="func-area">
<div class="func-area hide">
<ul class="search-suggest"></ul>
<div class="search-2016">
<form action="//search.yohobuy.com" method="get" id="search-form">
... ... @@ -108,7 +108,8 @@
</div>
<div class="nav-wrapper clearfix">
<div class="center-content">
<ul class="sub-nav-list">
{{# subNavGroup}}
<ul class="sub-nav-list {{subType}}">
{{# subNav}}
<li {{#if thirdNav}}class="contain-third"{{/if}}>
<a href="{{link}}">{{name}}
... ... @@ -141,6 +142,7 @@
</li>
{{/ subNav}}
</ul>
{{/ subNavGroup}}
</div>
</div>
</div>
... ... @@ -272,4 +274,4 @@
</div>
<input id="api-domain" type="hidden" value="{{apiDomain}}">
{{/ headerData}}
\ No newline at end of file
{{/ headerData}}
... ...
... ... @@ -11,4 +11,9 @@
</p>
</div>
\{{/ latestWalk}}
</script>
\ No newline at end of file
</script>
<div class="lazy-load-object">
<textarea class="latest-walk-datalazyload" style="visibility: hidden;">
<script> fetchLatestWalk(); </script>
</textarea>
</div>
... ...
... ... @@ -4,5 +4,6 @@
<h2>最近浏览的商品</h2>
<div id="latest-walk-goods" class="goods clearfix"></div>
</div>
{{> product/latest-walk-tpl}}
{{/if}}
\ No newline at end of file
{{/if}}
... ...
... ... @@ -39,6 +39,7 @@
"express": "^4.13.1",
"handlebars": "^4.0.5",
"express-handlebars": "^3.0.0",
"express-session": "^1.13.0",
"influxdb-winston": "^1.0.1",
"lodash": "^4.13.1",
"md5": "^2.1.0",
... ... @@ -103,7 +104,6 @@
"yoho-handlebars": "^4.0.5",
"yoho-jquery": "^1.12.4",
"yoho-jquery-lazyload": "^1.9.7",
"yoho-jquery-pjax": "0.0.1",
"yoho-jquery-placeholder": "^2.3.1",
"yoho-slider": "0.0.2",
"yoho-jquery-dotdotdot": "0.0.1",
... ...
/**
* 领券频道
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/04/14
*/
var $ = require('yoho-jquery'),
lazyLoad = require('yoho-jquery-lazyload'),
Dialog = require('../common/dialog').Dialog;
var alertConfig,
makeAlert;
// j面跳转对象
var redirect = {
// 去逛逛跳转链接
gunangSrc: null,
// 查看优惠券跳转链接
checkCouponSrc: null,
goToGuang: function() {
window.location.href = this.gunangSrc;
},
goToCheck: function() {
window.location.href = this.checkCouponSrc;
},
200: function(url) {
this.checkCouponSrc = url;
}
};
require('../common');
require('../plugins/slider');
// 加载底部图片
lazyLoad($('img.lazy'));
$('.slide-container').slider();
// 根据配置执行展示弹窗
function couponAlert(opt) {
var newAlert = new Dialog(opt);
newAlert.show();
}
// 配置弹窗
alertConfig = {
success: {
content: '恭喜您,成功领取优惠券',
subContents: ['特殊情况下到账有延时', '请耐心等待'],
className: 'subcontent-dialog',
refreshOnClose: true,
btns: [{
id: 1,
name: '去购物啦',
btnClass: ['black', 'btn-close'],
cb: function() {
redirect.goToGuang();
}
}, {
id: 2,
name: '查看优惠券',
btnClass: ['btn-close'],
cb: function() {
redirect.goToCheck();
}
}]
},
alreadyGot: {
content: '您已领取过优惠券',
subContent: '快去选购心仪的潮品吧',
className: 'subcontent-dialog',
btns: [{
id: 1,
name: '去使用',
btnClass: ['btn-close'],
cb: function() {
redirect.goToGuang();
}
}]
},
expired: {
content: '优惠券已过期',
subContent: '去领最新的优惠券吧',
className: 'subcontent-dialog',
btns: [{
id: 1,
name: '关闭',
btnClass: ['btn-close']
}]
},
paramerror: {
content: '请求参数出错',
subContents: ['请检查参数后重试'],
className: 'subcontent-dialog',
btns: [{
id: 1,
name: '关闭',
btnClass: ['btn-close']
}]
},
failed: {
content: '领取失败',
subContents: ['请刷新重试,', '多次无效请联系客服'],
className: 'subcontent-dialog',
btns: [{
id: 1,
name: '刷新',
btnClass: ['btn-close'],
cb: function() {
window.location.reload();
}
}]
}
};
// 对应不同的网络返回码展示不同的弹窗
makeAlert = {
200: function() {
couponAlert(alertConfig.success);
},
401: function() {
couponAlert(alertConfig.alreadyGot);
},
315: function() {
couponAlert(alertConfig.expired);
},
300: function() {
couponAlert(alertConfig.paramerror);
},
500: function() {
couponAlert(alertConfig.failed);
}
};
function requestCoupon(id) {
$.ajax({
type: 'GET',
url: '/coupon/sendcoupon',
data: {
id: id
},
success: function(res) {
var code = res.code;
if (code === 400) {
window.location.href = res.data.refer; // 未登录情况下,跳转到登录页
return;
}
// 如果返回的数据里有url,则执行redirect里的方法
res.url && res.url.length > 0 && redirect[code] && redirect[code](res.url + '?' + location.href.split('?')[1]);
// 如果能找到对应code则执行相应方法,如果没有的对应code则执行error提示
makeAlert[code] ? makeAlert[code]() : makeAlert['500']();
},
error: function() {
var Alert = require('../common/dialog').Alert;
new Alert('网络异常').show();
}
});
}
function getCouponStatus() {
var hash,
data = {},
search = window.location.search,
hashes = search ? decodeURIComponent(search).slice(1).split('&') : [];
for (i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
data[hash[0]] = hash[1];
}
if (!data.contentCode) {
return;
}
$.ajax({
type: 'GET',
url: '/coupon/couponstatus',
data: data,
success: function(res) {
if (res.code === 200) {
var cates = res.categories || [];
cates.forEach(function(obj) {
var e = document.getElementById(obj.id);
var child = e.children;
if (!child.length) {
return;
}
e.classList.remove('enable');
for (var i = 0; i < child.length; i++) {
if (child[i].className === 'normal') {
child[i].classList.add('hidden');
}
if ((obj.got && child[i].className.indexOf('got') > -1) || (obj.empty && child[i].className.indexOf('empty') > -1)) {
child[i].classList.remove('hidden');
}
}
})
}
}
});
}
// 获取领券状态
getCouponStatus();
$('.info').on('click', function(e) {
if (this.className.indexOf('enable') > -1) {
e.preventDefault();
requestCoupon($(this).closest('a').data('id'));
redirect.gunangSrc = $(this).closest('a').get(0).href;
}
});
\ No newline at end of file
... ...