Authored by biao

Merge branch 'release/1.0' of http://git.yoho.cn/fe/yoho-blk into release/1.0

const model = require('../models');
exports.index = (req, res) => {
res.display('index', Object.assign({
module: 'channel',
page: 'home'
}, model.getContent()));
const channelType = req.path.substring(1) || 'men';
model.getContent(channelType).then(result => {
res.display('index', Object.assign({
module: 'channel',
page: 'home'
}, result));
});
};
... ...
/**
* 频道页
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/7/26
*/
'use strict';
// const api = global.yoho.API;
const api = global.yoho.ServiceAPI;
const channelMap = require('../../../config/channel-type').channelMap;
const getChannelDataAsync = type => {
const data = {
client_type: 'web',
content_code: channelMap[type].code,
gender: channelMap[type].gender,
page: 1,
limit: 1000
};
return api.get('operations/api/v5/resource/home', data, {
cache: true,
code: 200
});
};
module.exports = {
getChannelDataAsync
};
... ...
const getContent = () => {
const content = {
/**
* 频道页 model
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/07/26
*/
'use strict';
const channelApi = require('./channel-api');
const camelCase = global.yoho.camelCase;
const _ = require('lodash');
/**
* 获取slider楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getSliderData = d => {
return {
slider: d
};
};
/**
* 获取BrandsAd楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getBrandAdFloor = d => {
_.forEach(d, data => {
data.btnText = 'shop now';
});
return {
brandsAd: d
};
};
/**
* 获取new arrivals楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getNewArrivals = d => {
_.forEach(d, (data, index) => {
if (index === 0 || index === d.length - 1) {
data.smallImg = true;
}
if (index % 2 === 0) {
data.even = true;
}
});
return {
floorZh: '新品抢鲜看',
floorEn: 'NEW ARRIVALS',
newArrivals: d
};
};
/**
* 获取classic brands楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getClassicBrands = d => {
let brands = [];
let subArr;
let i = 0;
_.forEach(d, (data, index) => {
if (index === 0 || index === 1 || index === 6 || index === 7) {
brands.push({
big: [d[index]]
});
} else if ((index > 1 && index < 6 || index > 7 && index < 12) && index % 2 === 0) {
if (i < 4) {
subArr = d.slice(index, index + 2);
brands[i].small = subArr;
i += 1;
}
}
});
_.forEach(brands, (data, index) => {
if (index < 2) {
data.bottomSpace = true;
}
if (index === 1 || index === 3) {
data.right = true;
}
});
return {
floorZh: '经典品牌',
floorEn: 'CLASSIC BRANDS',
classicBrands: brands
};
};
/**
* 获取潮流标志楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getStyleIcon = d => {
_.forEach(d, data => {
data.btnText = '去看看';
});
return {
floorZh: '潮流标志',
floorEn: 'STYLE ICON',
styleIcon: d
};
};
/**
* 获取咨询楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getEditorial = d => {
let e = {
big: {},
small: []
};
_.forEach(d, (data, index) => {
if (index === 0) {
e.big = data;
} else {
e.small.push(data);
}
});
_.forEach(e.small, (data, index) => {
if (index === 0 || index === 1) {
data.bottomSpace = true;
}
if (index === 0 || index === 2) {
data.rightSpace = true;
}
});
return {
floorZh: '资讯',
floorEn: 'EDITORIAL',
editorial: e
};
};
const floorMap = {
slider: _getSliderData,
标题: _getBrandAdFloor,
新品抢先看: _getNewArrivals,
CLASSIC: _getClassicBrands,
潮流标识: _getStyleIcon,
EDITORIAL: _getEditorial
};
/**
* 获取floorMap中对应的key
* @param {String} d 含有key的字符串
* @return {String} 得到的key值
*/
const _getKey = d => {
let k = d.split(' ')[0];
return k;
};
/**
* 判断title类型是否为对象
* @param {Object} t title
* @return {Boolen}
*/
const _isObjectTitle = t => {
return _.isObject(t);
};
/**
* 判断是否为Banner焦点图楼层
* @param {Object} d 楼层数据
* @return {Boolen}
*/
const _isBannerFloor = d => {
return d.templateName === 'focus' && d.templateIntro === '焦点图';
};
/**
* 获取用于渲染模板的数据
* @param {Object} d 接口返回的数据
* @return {Array} 模板数据数组
*/
const _processFloorData = d => {
let floorList = [];
_.forEach(d, data => {
let floorTitle;
let floorData;
// 处理banner
if (_isBannerFloor(data)) {
floorData = floorMap.slider(data.data);
// 判断标题类型
} else if (_isObjectTitle(data.data.title)) {
floorTitle = _getKey(data.data.title.title);
floorData = floorMap[floorTitle](data.data.list);
} else {
floorTitle = _getKey(data.data.title);
floorData = floorMap[floorTitle](data.data.list);
}
floorList.push(floorData);
});
return floorList;
};
const getContent = type => {
return channelApi.getChannelDataAsync(type).then(result => {
const l = camelCase(result.data.list);
const floor = {
content: _processFloorData(l)
};
return floor;
});
/* eslint-disable */
const content = {
content: [
{
slider: [
... ... @@ -250,7 +485,7 @@ const getContent = () => {
};
/* eslint-enable */
return content;
// return content;
};
module.exports = {
... ...
<div class="ad-banner">
{{#adBanner}}
<a href="{{link}}">
<a href="{{url}}">
<img class="lazy-img" data-original="{{image img 1150 160}}" alt="">
</a>
{{/adBanner}}
... ...
<div class="ad-container clearfix">
{{# brandsAd}}
<div class="ad {{#if @first}}first{{/if}}">
<img class="lazy-img" data-original="{{image img 240 240}}" alt="">
<img class="lazy-img" data-original="{{image src 240 240}}" alt="">
{{> brand-text-box}}
</div>
{{/ brandsAd}}
... ...
{{# classicBrands}}
<div class="brand-img-box {{#if right}}right{{/if}} {{#if bottomSpace}}mb10{{/if}}">
<a href="{{link}}">
<a href="{{url}}">
{{# big}}
<img class="big-img lazy-img" data-original="{{image img 556 333}}" alt="big-img">
<img class="big-img lazy-img" data-original="{{image src 556 333}}" alt="big-img">
{{/ big}}
{{# small}}
<img class="small-img lazy-img {{#if @first}}first{{/if}}" data-original="{{image img 283 283}}" alt="big-img">
<img class="small-img lazy-img {{#if @first}}first{{/if}}" data-original="{{image src 283 283}}" alt="big-img">
{{/ small}}
</a>
</div>
... ...
<div class="brand-text-box">
<h4>{{name}}</h4>
<p>{{des}}</p>
<button>{{btnText}}</button>
<h4>{{productName}}</h4>
<p>{{productDesc}}</p>
<a href="{{url}}">
<button>{{btnText}}</button>
</a>
</div>
... ...
... ... @@ -4,15 +4,15 @@
{{# editorial}}
<div class="news left">
{{# big}}
<a href="{{link}}">
<img class="lazy-img" data-original="{{image img 395 495}}" alt="">
<a href="{{url}}">
<img class="lazy-img" data-original="{{image src 395 495}}" alt="">
</a>
{{/ big}}
</div>
<div class="news right">
{{# small}}
<a href="{{link}}">
<img class="lazy-img {{#if bottomSpace}}bottom-space{{/if}} {{#if rightSpace}}right-space{{/if}}" data-original="{{image img 360 240}}" alt="">
<a href="{{url}}">
<img class="lazy-img {{#if bottomSpace}}bottom-space{{/if}} {{#if rightSpace}}right-space{{/if}}" data-original="{{image src 360 240}}" alt="">
</a>
{{/ small}}
</div>
... ...
... ... @@ -4,22 +4,22 @@
{{# newArrivals}}
<div class="arrival-item {{#if smallImg}}small-img{{/if}} {{#if @last}}last{{^}}normal{{/if}}">
{{#if even}}
<a href="{{link}}">
<img class="lazy-img" data-original="{{#if smallImg}}{{image img 223 490}}{{^}}{{image img 325 490}}{{/if}}" alt="">
<a href="{{url}}">
<img class="lazy-img" data-original="{{#if smallImg}}{{image src 223 490}}{{^}}{{image src 325 490}}{{/if}}" alt="">
</a>
<div class="brand-name">
<a href="{{link}}">
<a href="{{url}}">
<span class="bottom">{{name}}</span>
</a>
</div>
{{^}}
<div class="brand-name">
<a href="{{link}}">
<a href="{{url}}">
<span class="top">{{name}}</span>
</a>
</div>
<a href="{{link}}">
<img class="lazy-img" data-original="{{#if smallImg}}{{image img 223 490}}{{^}}{{image img 325 490}}{{/if}}" alt="">
<a href="{{url}}">
<img class="lazy-img" data-original="{{#if smallImg}}{{image src 223 490}}{{^}}{{image src 325 490}}{{/if}}" alt="">
</a>
{{/if}}
</div>
... ...
... ... @@ -4,7 +4,7 @@
{{# slider}}
<li style="{{#if bgColor}}background:{{bgColor}}{{/if}}">
<a href="{{url}}" target= "_blank">
<img class="lazy" data-original="{{image img 1920 645}}" alt="">
<img class="lazy" data-original="{{image src 1920 645}}" alt="">
</a>
{{# tips}}
<div class="slide-tips">
... ... @@ -23,7 +23,7 @@
{{# pagination}}
<li>
<a href="{{url}}" target="_blank"></a>
<img src="{{image img 138 54}}" alt="">
<img src="{{image src 138 54}}" alt="">
</li>
{{/ pagination}}
</ul>
... ...
... ... @@ -3,7 +3,7 @@
<div class="smooth-card-container">
{{# styleIcon}}
<div class="card {{#if @first}}active{{/if}} {{#if @last}}last{{/if}}">
<img class="img lazy-img" data-original="{{image img 221 320}}" />
<img class="img lazy-img" data-original="{{image src 221 320}}" />
{{> brand-text-box}}
</div>
{{/ styleIcon}}
... ...
... ... @@ -359,12 +359,13 @@ const _setRefundDetailData = (data) => {
let goods = [];
_.forEach(data.goods_list, value => {
let cnAlphabet = value.cn_alphabet ? value.cn_alphabet : '';
goods.push({
href: helpers.urlFormat(`/product/pro_${value.product_id}_${value.goods_id}/${cnAlphabet}.html`),
img: value.goods_image,
name: value.product_name,
name: _.truncate(value.product_name, {
length: 34,
omission: '...'
}),
title: value.product_name,
size: value.size_name,
color: value.color_name,
num: 1, // 接口目前不支持
... ...
... ... @@ -36,8 +36,10 @@
</p>
<p>
我们会在入库后的1-3个工作日内处理您的退款,如有疑问,请联系
<span class="iconfont">&#xe61c;</span>
<span>在线客服</span>
<a href="http://chat8.live800.com/live800/chatClient/chatbox.jsp?companyID=620092&configID=149091&jid=8732423409" class="online-service">
<span class="iconfont blue">&#xe61c;</span>
<span class="blue">在线客服</span>
</a>
</p>
</div>
{{/ backStorage}}
... ... @@ -118,12 +120,10 @@
{{# goods}}
<div class="goods-item clearfix">
<div class="img">
<a href="{{href}}" title="{{name}}" target="_blank">
<img class="lazy" data-original="{{image img 70 90}}">
</a>
<img class="lazy" title="{{title}}" data-original="{{image img 70 90}}">
</div>
<div class="info">
<p><a href="{{href}}" class="title" title="{{name}}" target="_blank">{{name}}</a></p>
<p><span title="{{title}}">{{name}}</span></p>
<p>颜色:{{color}}&nbsp;尺码:{{size}}</p>
<p>×{{num}}</p>
</div>
... ...
... ... @@ -43,9 +43,11 @@ const Query = {
});
if (data.filter) {
data.filter.standard = data.standard;
data.filter.groupSort = DataHelper.sortConvert(allSort.data.sort);
retData.filter = DataHelper.filterHandle(data.filter, q);
retData.filter.showPrice = data.total > 10;
retData.filter.showInfo = retData.filter.style || data.standard;
}
retData.navPath = {
... ...
... ... @@ -309,6 +309,33 @@ const helpers = {
});
}
if (q.style) {
let styleNames = filter.style.filter(s => {
return (',' + q.style + ',').indexOf(',' + s.styleId + ',') >= 0;
}).map(s => {
s.checked = true;
return s.styleName;
}).join(',');
if (styleNames) {
styleNames = styleNames.length > 10 ? styleNames.substr(0, 10) + '...' : styleNames;
filters.push(this.newFilter('style', q.style, styleNames));
}
}
if (q.standard) {
let standardIds = q.standard.split(',');
filter.standard.forEach(s => {
s.sub.forEach(b => {
if (b.standardId === parseInt(standardIds[1], 10)) {
b.checked = true;
filters.push(this.newFilter('standard', q.standard, b.standardName));
}
});
});
}
return {
people: genders,
sortData: sorts,
... ... @@ -316,6 +343,8 @@ const helpers = {
brandData: brands,
colors: colors,
size: sizeInfo,
style: filter.style,
standard: filter.standard,
priceRange: priceRange,
filters: filters,
showFilters: filters.length > 0,
... ...
... ... @@ -29,6 +29,13 @@ function _paramHanlde(obj) {
}
}
if (obj.standard) {
let standards = obj.standard.split(',');
obj.standard_id = standards[0];
obj.standard_value_id = standards[1];
}
return obj;
}
... ...
... ... @@ -138,19 +138,67 @@
{{/if}}
{{#if colors}}
<div class="yoho-ui-accordion">
<h3>颜色</h3>
<div class="body">
<div class="color-body nano">
<div class="nano-content">
{{#each colors}}
<div class="input-radio" data-value="{{id}}">
{{> round-color}}
<label>{{title}}</label>
</div>
{{/each}}
<div class="yoho-ui-accordion">
<h3>颜色</h3>
<div class="body">
<div class="color-body nano">
<div class="nano-content">
{{#each colors}}
<div class="input-radio" data-value="{{id}}">
{{> round-color}}
<label>{{title}}</label>
</div>
{{/each}}
</div>
</div>
</div>
</div>
</div>
{{/if}}
{{#if showInfo}}
<div class="blank-div"></div>
<div class="title">商品属性</div>
{{#if style}}
<div class="yoho-ui-accordion">
<h3>风格</h3>
<div class="body">
<div class="style-mulit-btn">
<span class="btn white">多选</span>
</div>
<div class="style-body nano">
<div class="nano-content">
{{#each style}}
<div class="input-radio" data-value="{{styleId}}">
{{> icon/radio }}
<label>{{styleName}}</label>
</div>
{{/each}}
</div>
</div>
<div class="style-btns hide">
<span class="btn disable large confirm">确定</span>
<span class="btn large cancel">取消</span>
</div>
</div>
{{#if standard}}
{{#each standard}}
<h3>{{standardName}}</h3>
<div class="body" data-value="{{standardId}}">
<div class="standard-body nano">
<div class="nano-content">
{{#each sub}}
<div class="input-radio" data-value="{{standardId}}">
{{> icon/radio }}
<label>{{standardName}}</label>
</div>
{{/each}}
</div>
</div>
</div>
{{/each}}
{{/if}}
</div>
{{/if}}
{{/if}}
... ...
/**
* 共用的map数据常量定义
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/07/26
*/
'use strict';
const channelMap = {
men: {
code: '39c103615558890846e56749af040228',
gender: '1,3'
},
women: {
code: '527079e6c46d0f125eb46b835968971b',
gender: '2,3'
},
lifestyle: {
code: '94b5ed607b6d565ffc29c2c04be121dc',
gender: ''
}
};
const meCode = '02889429674251b93f2add429008e1f8';
module.exports = {
channelMap,
meCode
};
... ...
... ... @@ -9,6 +9,7 @@ var YohoListPage = {
rootDoc: $('.yoho-product-list'),
brandsDoc: $('.yoho-product-list .brand-list'),
mulitBrand: false,
mulitStyle: false,
goodsWrapper: $('.goods-area .goods-wrapper'),
goodsWrapperState: false,
page: query.page || 1,
... ... @@ -41,7 +42,8 @@ var YohoListPage = {
YohoListPage.go({
msort: categoryId,
misort: subCategoryId
misort: subCategoryId,
page: 1
});
}
});
... ... @@ -82,6 +84,36 @@ var YohoListPage = {
YohoListPage.go({color: $(this).data('value')});
});
$('.style-body .input-radio', this.rootDoc).check({
type: 'radio',
group: 'style',
onChange: function(ele, checked, value) {
if (!YohoListPage.mulitStyle) {
YohoListPage.go({style: value});
} else {
$('.style-btns', this.rootDoc).removeClass('hide');
if ($('.style-body .input-radio', this.rootDoc).check('getChecked').values.length > 0) {
$('.style-btns .confirm', this.rootDoc).removeClass('disable');
} else {
$('.style-btns .confirm', this.rootDoc).addClass('disable');
}
}
}
});
$('.standard-body .input-radio', this.rootDoc).check({
type: 'radio',
group: 'standard',
onChange: function(ele, checked, value) {
var parentId = $(ele).parents('.body').data('value');
YohoListPage.go({
standard: parentId + ',' + value
});
}
});
YohoListPage.eventBind();
},
initAccordion() {
... ... @@ -138,6 +170,18 @@ var YohoListPage = {
}
});
$('.style-btns .cancel', this.rootDoc).click(function() {
YohoListPage.closeStyleMulit();
});
$('.style-btns .confirm', this.rootDoc).click(function() {
if (!$(this).hasClass('disable')) {
YohoListPage.go({
style: $('.style-body .input-radio', YohoListPage.rootDoc).check('getChecked').values.join(',')
});
}
});
$('.yoho-product-list .brand-body input').on('keyup', function() {
YohoListPage.filterBrand($(this).val().toLowerCase());
});
... ... @@ -235,6 +279,14 @@ var YohoListPage = {
});
}
});
$('.style-mulit-btn .btn', this.rootDoc).click(function() {
if ($(this).hasClass('white')) {
YohoListPage.openStyleMulit();
} else {
YohoListPage.closeStyleMulit();
}
});
},
openBrandMulitChoose: function() {
$('.yoho-product-list .mulit-choose').hide();
... ... @@ -254,6 +306,26 @@ var YohoListPage = {
type: 'radio'
});
},
openStyleMulit: function() {
$('.style-btns', this.rootDoc).removeClass('hide');
$('.style-body .input-radio', this.rootDoc).check({
type: 'checkbox'
});
YohoListPage.mulitStyle = true;
$('.style-mulit-btn .btn').removeClass('white');
},
closeStyleMulit: function() {
$('.style-btns', this.rootDoc).addClass('hide');
$('.style-body .input-radio', this.rootDoc).check('unCheckAll');
$('.style-body .input-radio', this.rootDoc).check({
type: 'radio'
});
YohoListPage.mulitStyle = false;
$('.style-mulit-btn .btn').addClass('white');
},
showBrandMulitBtn: function() {
$('.brand-btns', this.rootDoc).removeClass('hide');
if (YohoListPage.getSelectBrands().length > 0) {
... ...
... ... @@ -277,14 +277,14 @@ var Cart = {
* @params { Function } callback 移入收藏夹成功后回调
*/
sendToFavorite: function(products, callback) {
var msg = '确定要将该商品从购物车中移入收藏吗?<br/>移入收藏后该商品将不在购物车中显示';
var msg = '<p>确定要将该商品从购物车中移入收藏吗?</p><p>移入收藏后该商品将不在购物车中显示</p>';
var dialog;
if (!products.length) {
new _alert('请至少选中一件商品!').show();
} else {
if (products.length > 1) {
msg = '确定要将已选中的商品从购物车中移入收藏吗?<br/>移入收藏后已选中的商品将不在购物车中显示';
msg = '<p>确定要将已选中的商品从购物车中移入收藏吗?</p><p>移入收藏后已选中的商品将不在购物车中显示</p>';
}
// callback存在说明从删除模块收藏
... ...
... ... @@ -29,10 +29,12 @@
&.top {
line-height: 30px;
position: static !important;
}
&.bottom {
line-height: 56px;
position: static !important;
}
}
}
... ...
... ... @@ -40,6 +40,14 @@
.cancel-btn {
display: inline-block;
}
.online-service {
margin-left: 14px;
> * {
font-size: 14px;
}
}
}
.return-express {
... ... @@ -125,10 +133,6 @@
padding-left: 20px;
text-align: left;
}
.title:hover {
text-decoration: underline;
}
}
.goods-item {
... ...
... ... @@ -60,10 +60,32 @@
.list-body,
.size-body,
.color-body,
.brand-list {
.brand-list,
.standard-body {
max-height: 240px;
overflow-y: auto;
}
.style-body {
max-height: 240px;
overflow-y: auto;
.input-radio {
display: inline-block;
width: 80px;
}
}
.style-mulit-btn {
margin: 15px 0;
.btn {
width: 50px;
}
.btn.white {
color: #111;
}
}
.brand-body {
margin: 5px 0;
.brand-search {
... ... @@ -97,7 +119,8 @@
cursor: pointer;
}
}
.brand-btns {
.brand-btns,
.style-btns {
text-align: center;
margin-top: 10px;
... ...
... ... @@ -63,7 +63,9 @@
i {
position: absolute;
right: -6px;
right: -16px\9; /* stylelint-disable-line */
top: 0;
top: -2px\9; /* stylelint-disable-line */
width: 0;
height: 0;
line-height: 0;
... ...