Authored by htoooth

Merge remote-tracking branch 'origin/release/1.0' into release/1.0

Showing 74 changed files with 969 additions and 747 deletions
{{#each category}}
<dl class="clearfix" name="{{key}}">
<dt>{{key}}</dt>
<dt class="brand-key">{{key}}</dt>
<dd class="brand-tabs">
<ul class="clearfix">
{{#each brands}}
... ...
/**
* 频道页controller
* @author: 赵彪
* @date: 2016/07/13
*/
'use strict';
const model = require('../models');
exports.index = (req, res) => {
... ...
/**
* 获取各个楼层的数据
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/08/05
*/
'use strict';
const _ = require('lodash');
/**
* 获取楼层title
* @param {String || Object} 原始title
* @return {Object} 转换后的title
*/
const _getTitle = t => {
const reg = /\w+/g;
let arr = [];
let r;
let cn;
let en;
if (!t) {
return {
en: '',
cn: ''
};
}
if (_.isObject(t)) {
t = t.title;
}
do {
r = reg.exec(t);
if (r) {
arr.push(r[0]);
}
} while (r);
en = arr.join(' ');
cn = t.replace(en, '');
return {
en,
cn
};
};
/**
* 获取slider楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getSliderData = d => {
return {
slider: d
};
};
/**
* 获取BrandsAd楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getBrandAdFloor = d => {
const list = d.list;
_.forEach(list, data => {
data.btnText = 'shop now';
});
return {
brandsAd: list
};
};
/**
* 获取new arrivals楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getNewArrivals = d => {
const list = d.list;
const title = _getTitle(d.title);
_.forEach(list, (data, index) => {
if (index === 0 || index === list.length - 1) {
data.smallImg = true;
}
if (index % 2 !== 0) {
data.even = true;
}
});
return {
floorZh: title.cn,
floorEn: title.en,
newArrivals: list
};
};
/**
* 获取classic brands楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getClassicBrands = d => {
let brands = [];
let subArr;
let i = 0;
const list = d.list;
const title = _getTitle(d.title);
_.forEach(list, (data, index) => {
if (index === 0 || index === 1 || index === 6 || index === 7) {
brands.push({
big: [list[index]]
});
} else if ((index > 1 && index < 6 || index > 7 && index < 12) && index % 2 === 0) {
if (i < 4) {
subArr = list.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: title.cn,
floorEn: title.en,
classicBrands: brands
};
};
/**
* 获取潮流标志楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getStyleIcon = d => {
const list = d.list;
const title = _getTitle(d.title);
_.forEach(list, data => {
data.btnText = '去看看';
});
return {
floorZh: title.cn,
floorEn: title.en,
styleIcon: list
};
};
/**
* 获取广告楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getAdBanner = d => {
return {
adBanner: d
};
};
/**
* 获取咨询楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const getEditorial = d => {
const list = d.list;
const title = _getTitle(d.title);
let e = {
big: {},
small: []
};
_.forEach(list, (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: title.cn,
floorEn: title.en,
editorial: e
};
};
module.exports = {
getSliderData,
getBrandAdFloor,
getNewArrivals,
getClassicBrands,
getStyleIcon,
getAdBanner,
getEditorial
};
... ...
... ... @@ -6,199 +6,90 @@
'use strict';
const channelApi = require('./channel-api');
const floorDataHandler = require('./floor-data-handler');
const camelCase = global.yoho.camelCase;
const _ = require('lodash');
/**
* 获取slider楼层数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getSliderData = d => {
return {
slider: d
};
};
/**
* 获取BrandsAd楼层数据
* 获取模板名为single_image的数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getBrandAdFloor = d => {
_.forEach(d, data => {
data.btnText = 'shop now';
});
return {
brandsAd: d
};
const _processSingleImage = (d) => {
return floorDataHandler.getAdBanner(d);
};
/**
* 获取new arrivals楼层数据
* 获取模板名为focus的数据
* @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
};
const _processFocus = (d) => {
return floorDataHandler.getSliderData(d);
};
/**
* 获取classic brands楼层数据
* 获取模板名为blk_brand的数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getClassicBrands = d => {
let brands = [];
let subArr;
let i = 0;
const _processBlkBrand = (d) => {
let len;
_.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;
}
}
});
if (!d.list) {
return false;
}
_.forEach(brands, (data, index) => {
if (index < 2) {
data.bottomSpace = true;
}
if (index === 1 || index === 3) {
data.right = true;
}
});
len = d.list.length;
return {
floorZh: '经典品牌',
floorEn: 'CLASSIC BRANDS',
classicBrands: brands
};
if (len === 2) {
return floorDataHandler.getBrandAdFloor(d);
} else if (len === 4) {
return floorDataHandler.getStyleIcon(d);
}
};
/**
* 获取潮流标志楼层数据
* 获取模板名为image_list的数据
* @param {Object} d 接口返回的楼层数据
* @return {Object} 处理之后的数据
*/
const _getStyleIcon = d => {
_.forEach(d, data => {
data.btnText = '去看看';
});
const _processImageList = (d) => {
let len;
return {
floorZh: '潮流标志',
floorEn: 'STYLE ICON',
styleIcon: d
};
};
if (!d.list) {
return false;
}
const _getAdBanner = d => {
return {
adBanner: d
};
len = d.list.length;
if (len === 4) {
return floorDataHandler.getNewArrivals(d);
} else if (len === 5) {
return floorDataHandler.getEditorial(d);
}
};
/**
* 获取咨询楼层数据
* 获取模板名为recommend_content_five的数据
* @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 _processRecommendContentFive = (d) => {
return floorDataHandler.getClassicBrands(d);
};
const floorMap = {
slider: _getSliderData,
标题: _getBrandAdFloor,
NEW: _getNewArrivals,
CLASSIC: _getClassicBrands,
STYLE: _getStyleIcon,
EDITORIAL: _getEditorial,
adBanner: _getAdBanner
};
/**
* 获取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);
// 根据templete_name字段找到不同的处理方法
const templateMap = {
single_image: _processSingleImage,
blkBrand: _processBlkBrand,
image_list: _processImageList,
focus: _processFocus,
recommend_content_five: _processRecommendContentFive
};
/**
* 判断是否为Banner焦点图楼层
* @param {Object} d 楼层数据
* @return {Boolen}
*/
const _isBannerFloor = d => {
return d.templateName === 'focus' && d.templateIntro === '焦点图';
};
/**
* 获取用于渲染模板的数据
... ... @@ -209,32 +100,14 @@ const _processFloorData = d => {
let floorList = [];
_.forEach(d, data => {
let floorTitle;
let floorData;
if (!data.data) {
return false;
}
// 处理banner
if (_isBannerFloor(data)) {
floorData = floorMap.slider(data.data);
// 判断标题类型
} else if (_isObjectTitle(data.data.title)) {
floorTitle = _getKey(data.data.title.title);
if (floorMap[floorTitle]) {
floorData = floorMap[floorTitle](data.data.list);
}
} else if (data.data.title) {
floorTitle = _getKey(data.data.title);
if (floorMap[floorTitle]) {
floorData = floorMap[floorTitle](data.data.list);
}
} else if (data.templateName === 'single_image') {
floorData = floorMap.adBanner(data.data);
if (templateMap[data.templateName]) {
floorData = templateMap[data.templateName](data.data);
}
floorList.push(floorData);
... ... @@ -243,6 +116,11 @@ const _processFloorData = d => {
return floorList;
};
/**
* 获取楼层数据
* @param {String} type 当前的频道
* @return {Object} 完整的楼层数据
*/
const getContent = type => {
return channelApi.getChannelDataAsync(type).then(result => {
if (result.data && result.data.list) {
... ... @@ -254,259 +132,6 @@ const getContent = type => {
return floor;
}
});
/* eslint-disable */
const content = {
content: [
{
slider: [
{
img: '//placehold.it/{width}x{height}',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/28/11/015a86ade17dc6213bab85b2162adebcd6.jpg?imageView2/2/w/1920/h/650',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/23/13/01ebff30179db84975c42a4f3c8b1f4d44.jpg?imageView2/1/w/1150/h/450',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/23/13/01ebff30179db84975c42a4f3c8b1f4d44.jpg?imageView2/1/w/1150/h/450',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/23/13/01ebff30179db84975c42a4f3c8b1f4d44.jpg?imageView2/1/w/1150/h/450',
link: '/'
},
{
img: '//placehold.it/{width}x{height}',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/28/11/015a86ade17dc6213bab85b2162adebcd6.jpg?imageView2/2/w/1150/h/450',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/28/11/015a86ade17dc6213bab85b2162adebcd6.jpg?imageView2/2/w/1150/h/450',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/28/11/015a86ade17dc6213bab85b2162adebcd6.jpg?imageView2/2/w/1150/h/450',
link: '/'
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/28/11/015a86ade17dc6213bab85b2162adebcd6.jpg?imageView2/2/w/1150/h/450',
link: '/'
}
]
},
{
brandsAd: [
{
img: '//placehold.it/{width}x{height}',
name: 'GINZA',
des: '藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场',
btnText: 'shop now'
},
{
img: '//placehold.it/{width}x{height}',
name: 'STUSSY',
des: '藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场藤原浩于银座停车场',
btnText: 'shop now'
}
]
},
{
floorZh: '新品抢鲜看',
floorEn: 'NEW ARRIVALS',
newArrivals: [
{
img: '//placehold.it/{width}x{height}',
name: 'STUSSY',
link: '/',
smallImg: true
},
{
img: '//placehold.it/{width}x{height}',
name: 'DAILY PAPER',
link: '/',
even: true
},
{
img: '//placehold.it/{width}x{height}',
name: 'BAPE',
link: '/'
},
{
img: '//placehold.it/{width}x{height}',
name: 'SUPREME',
link: '/',
even: true,
smallImg: true
}
]
},
{
floorZh: '经典品牌',
floorEn: 'CLASSIC BRANDS',
classicBrands: [
{
big: [
{
img: '//placehold.it/{width}x{height}',
link: ''
}
],
small: [
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/30/10/01714bacda5e9fa323a1dc5f720a7f7140.jpg?imageView2/1/w/185/h/248',
link: ''
},
{
img: '//img10.static.yhbimg.com/yhb-img01/2016/06/30/10/01714bacda5e9fa323a1dc5f720a7f7140.jpg?imageView2/1/w/185/h/248',
link: ''
}
],
bottomSpace: true
},
{
big: [
{
img: '//placehold.it/{width}x{height}',
link: ''
}
],
small: [
{
img: '//placehold.it/{width}x{height}',
link: ''
},
{
img: '//placehold.it/{width}x{height}',
link: ''
}
],
right: true,
bottomSpace: true
},
{
big: [
{
img: '//placehold.it/{width}x{height}',
link: ''
}
],
small: [
{
img: '//placehold.it/{width}x{height}',
link: ''
},
{
img: '//placehold.it/{width}x{height}',
link: ''
}
]
},
{
big: [
{
img: '//placehold.it/{width}x{height}',
link: ''
}
],
small: [
{
img: '//placehold.it/{width}x{height}',
link: ''
},
{
img: '//placehold.it/{width}x{height}',
link: ''
}
],
right: true
}
]
},
{
floorZh: '潮流标志',
floorEn: 'STYLE ICON',
styleIcon: [
{
img: '//placehold.it/{width}x{height}',
name: 'COTE&CIEL',
des: '这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生',
link: '/ ',
btnText: '去看看'
},
{
img: '//placehold.it/{width}x{height}',
name: 'COTE&CIEL',
des: '这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生',
link: '/ ',
btnText: '去看看'
},
{
img: '//placehold.it/{width}x{height}',
name: 'COTE&CIEL',
des: '这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生',
link: '/ ',
btnText: '去看看'
},
{
img: '//placehold.it/{width}x{height}',
name: 'COTE&CIEL',
des: '这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生这都什么时代了?短发女生',
link: '/ ',
btnText: '去看看'
}
]
},
{
adBanner: {
img: '//placehold.it/{width}x{height}',
link: ''
}
},
{
floorZh: '资讯',
floorEn: 'EDITORIAL',
editorial: {
big: {
img: '//placehold.it/{width}x{height}',
link: ''
},
small: [
{
img: '//placehold.it/{width}x{height}',
link: '/',
bottomSpace: true,
rightSpace: true
},
{
img: '//placehold.it/{width}x{height}',
link: '/',
bottomSpace: true
},
{
img: '//placehold.it/{width}x{height}',
link: '/',
rightSpace: true
},
{
img: '//placehold.it/{width}x{height}',
link: ''
}
]
}
}
]
};
/* eslint-enable */
// return content;
};
module.exports = {
... ...
<div class="ad-banner">
{{#adBanner}}
<a href="{{url}}">
<a href="{{url}}" target="_blank">
<img class="lazy-img" data-original="{{image src 1150 160}}" title="{{title}}" alt="{{alt}}">
</a>
{{/adBanner}}
... ...
<div class="ad-container clearfix">
{{# brandsAd}}
<div class="ad {{#if @first}}first{{/if}}">
<a href="{{url}}">
<img class="lazy-img" data-original="{{image src 240 240}}" alt="">
<a href="{{url}}" target="_blank">
<img class="lazy-img" data-original="{{image src 298 298}}" alt="">
</a>
{{> brand-text-box}}
</div>
... ...
{{# classicBrands}}
<div class="brand-img-box {{#if right}}right{{/if}} {{#if bottomSpace}}mb10{{/if}}">
<a href="{{url}}">
<a href="{{url}}" target="_blank">
{{# big}}
<img class="big-img lazy-img" data-original="{{image src 556 333}}" alt="big-img">
<img class="big-img lazy-img" data-original="{{image src 565 340}}" alt="big-img">
{{/ big}}
{{# small}}
<img class="small-img lazy-img {{#if @first}}first{{/if}}" data-original="{{image src 283 283}}" alt="big-img">
<img class="small-img lazy-img {{#if @first}}first{{/if}}" data-original="{{image src 281 285}}" alt="big-img">
{{/ small}}
</a>
</div>
... ...
<div class="brand-text-box">
<h4>{{productName}}</h4>
<p>{{productDesc}}</p>
<a href="{{url}}">
<a href="{{url}}" target="_blank">
<button>{{btnText}}</button>
</a>
</div>
... ...
... ... @@ -4,16 +4,22 @@
{{# editorial}}
<div class="news left">
{{# big}}
<a href="{{url}}">
<img class="lazy-img" data-original="{{image src 395 495}}" alt="">
</a>
<span class="box">
<a href="{{url}}" target="_blank">
<img class="lazy-img" data-original="{{image src 395 495}}" alt="{{alt}}">
</a>
<p class="editorial-title">{{title}}</p>
</span>
{{/ big}}
</div>
<div class="news right">
{{# small}}
<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>
<span class="box {{#if bottomSpace}}bottom-space{{/if}} {{#if rightSpace}}right-space{{/if}}">
<a href="{{url}}" target="_blank">
<img class="lazy-img" data-original="{{image src 360 240}}" alt="{{alt}}">
</a>
<p class="editorial-title">{{title}}</p>
</span>
{{/ small}}
</div>
{{/ editorial}}
... ...
... ... @@ -4,8 +4,8 @@
{{# newArrivals}}
<div class="arrival-item {{#if smallImg}}small-img{{/if}} {{#if @last}}last{{^}}normal{{/if}}">
{{#if even}}
<a href="{{url}}">
<img class="lazy-img" data-original="{{#if smallImg}}{{image src 223 490}}{{^}}{{image src 325 490}}{{/if}}" alt="{{alt}}">
<a href="{{url}}" target="_blank">
<img class="lazy-img" data-original="{{#if smallImg}}{{image src 218 490}}{{^}}{{image src 327 490}}{{/if}}" alt="{{alt}}">
</a>
<div class="brand-name">
<a href="{{url}}">
... ... @@ -18,8 +18,8 @@
<span class="top">{{title}}</span>
</a>
</div>
<a href="{{url}}">
<img class="lazy-img" data-original="{{#if smallImg}}{{image src 223 490}}{{^}}{{image src 325 490}}{{/if}}" alt="{{alt}}">
<a href="{{url}}" target="_blank">
<img class="lazy-img" data-original="{{#if smallImg}}{{image src 218 490}}{{^}}{{image src 327 490}}{{/if}}" alt="{{alt}}">
</a>
{{/if}}
</div>
... ...
... ... @@ -246,6 +246,8 @@ const _processContentData = (list) => {
list = list || [];
list = camelCase(list);
// console.log(list)
let contentData = {
contents: []
};
... ... @@ -268,7 +270,7 @@ const _processContentData = (list) => {
let idList = '';
// let recoLength = '';
let recoLength = '';
let related = {
relatedReco: [],
relatedGroup: []
... ... @@ -283,11 +285,11 @@ const _processContentData = (list) => {
idList += data.id + ',';
related.relatedReco.push(data);
recoLength = recoLength + 1;
});
}
// recoLength = idList.length / 9;
if (value.goodsGroup) {
_.forEach(value.goodsGroup.data, function(data) {
... ... @@ -308,8 +310,8 @@ const _processContentData = (list) => {
_.forEach(related.relatedReco, function(data, index) {
data = _.assign(data, result[index]);
});
result.splice(0, recoLength.length);
}
if (related.relatedGroup) {
... ... @@ -330,6 +332,7 @@ const _processContentData = (list) => {
return contentData;
});
// console.log(contentData)
return contentData;
};
... ... @@ -435,7 +438,7 @@ const _getArticlePre = (id) => {
id: id
}).then((result) => {
if (result && result.code === 200) {
console.log(result);
// console.log(result);
return result;
} else {
logger.error('上一篇不是200');
... ... @@ -566,7 +569,7 @@ const getDetailData = (id) => {
tags: res.tags,
content: result[2],
comment: result[3],
brands: result[4]
brands: result[5]
};
})();
};
... ...
... ... @@ -60,9 +60,6 @@
</div>
</div>
{{!-- {{#if relatedGroup}} --}}
<div class="related-reco related-group">
<div class="article-title">
<div class="title-line"></div>
... ... @@ -72,12 +69,16 @@
</div>
</div>
</div>
<div class="related-goods goods">
{{# relatedGroup}}
{{> related-goods}}
{{/ relatedGroup}}
<div class="related-groups">
<div class="related-goods goods">
{{# relatedGroup}}
{{> related-goods}}
{{/ relatedGroup}}
</div>
</div>
<span class="left iconfont hide">&#xe62c;</span>
<span class="right iconfont">&#xe629;</span>
</div>
{{/ related}}
{{/ content}}
... ... @@ -101,29 +102,29 @@
</a>
<p class="brand-name">{{name}}</p>
</div>
{{/ brands}}
{{/ brands}}
</div>
</div>
{{/if}}
</div>
{{# userInfo}}
<div class="user-handle">
<ul class="clearfix">
<li id="prise-btn" class="like-status{{# isLike}} liked{{/ isLike}}">
<a href="javascript:;">
<i class="iconfont">&#xe60e;</i>
<span class="like-num">{{likeNum}}</span>
</a>
</li>
<li id="collect-btn" class="sort-collect{{# isCollected}} collected{{/ isCollected}}">
<a href="javascript:;">
<i class="iconfont">&#xe619;</i>
<span>收藏</span>
</a>
</li>
</ul>
</div>
<div class="user-handle">
<ul class="clearfix">
<li id="prise-btn" class="like-status{{# isLike}} liked{{/ isLike}}">
<a href="javascript:;">
<i class="iconfont">&#xe60e;</i>
<span class="like-num">{{likeNum}}</span>
</a>
</li>
<li id="collect-btn" class="sort-collect{{# isCollected}} collected{{/ isCollected}}">
<a href="javascript:;">
<i class="iconfont">&#xe619;</i>
<span>收藏</span>
</a>
</li>
</ul>
</div>
{{/ userInfo}}
{{> share}}
... ...
... ... @@ -65,6 +65,7 @@ const getOrderList = (req, res) => {
page: 'order',
isMe: true,
orderList: result.order.orderList,
paginationOpts: result.order.paginationOpts,
orderData: result.order.orderData
});
});
... ...
... ... @@ -16,15 +16,18 @@ const index = (req, res, next) => {
const uid = req.user.uid;
const page = req.query.page;
returns.getUserReturn(uid, page).then(result => {
Promise.all([returns.getUserReturn(uid, page), mcHandler.getMeThumb()]).then(result => {
const pageData = result[0];
const thumb = result[1];
res.display('index', {
page: 'return-list',
isMe: true,
content: Object.assign({
nav: mcHandler.getMeCrumb('我的退/换货'),
navigation: mcHandler.getSideMenu('我的退/换货'),
banner: 'http://placehold.it/{width}x{height}'
}, result)
banner: thumb
}, pageData)
});
}).catch(next);
};
... ...
... ... @@ -43,6 +43,7 @@ const btnMap = {
classStr: 'btn white cancel-btn mr'
},
{
isEditBtn: true,
name: '修改订单',
classStr: 'btn white edit-btn'
}
... ... @@ -518,6 +519,12 @@ const getOrderDetail = (uid, code) => {
}
});
if (detail.canUpdateDeliveryAddress === 'N') {
_.remove(detail.btns, btn => {
return btn.isEditBtn;
});
}
if (parseInt(detail.paymentType, 10) === 2 &&
(statusMap[st].valueStr === '备货中' ||
detail.statusStr === '备货中')) {
... ...
... ... @@ -86,8 +86,8 @@
{{!-- 在线客服和返回顶部 --}}
<div class="service-top">
<a class="service" href="http://chat8.live800.com/live800/chatClient/chatbox.jsp?companyID=703953&configID=149819&jid=1099911094" target="_blank">
<span class="iconfont">&#xe61c;</span>
<span class="hover-text hide">在线<br>客服</span>
<span class="iconfont hide">&#xe61c;</span>
<span class="hover-text">在线<br>客服</span>
</a>
<div class="return-top hide">
<span class="iconfont bold">&#xe617;</span>
... ...
... ... @@ -16,14 +16,15 @@
<td class="width-name">{{consignee}}</td>
<td class="width-address">{{area}}</td>
<td class="width-fulladdress">{{address}}</td>
<td class="width-mobile"><p>{{mobile}}</p><p>{{phone}}</p></td>
<td class="width-mobile"><p>{{mobile}}</p>
<p>{{phone}}</p></td>
<td class="width-opearte">
<div>
<span class="blue opreation update-address" data-id="{{address_id}}">修改</span>
<em class="op-sep">|</em>
<span class="blue opreation del-address" data-id="{{address_id}}">删除</span>
{{#if default}}
<span class="btn set-default opreation current-default ">默认地址</span>
<span class="btn set-default opreation current-default" data-id={{address_id}}>默认地址</span>
{{else}}
<span class="btn set-default opreation " data-id={{address_id}}>设为默认</span>
{{/if}}
... ...
... ... @@ -15,6 +15,5 @@
{{/each}}
</select>
<span class="blue">{{> icon/doubt}}什么是YOHO币</span>
</div>
</div>
... ...
... ... @@ -7,7 +7,7 @@
{{> order/goods-box}}
<div class="common-column special-border">
<p class="bold">¥{{amount}}</p>
<p class="subtext">{{paymentTypeStr}}</p>
<p class="subtext no-pointer">{{paymentTypeStr}}</p>
{{#if isRefundOrder}}
<p class="subtext refund-tag">换货订单</p>
{{/if}}
... ...
... ... @@ -19,6 +19,7 @@ const index = (req, res, next) => {
return next();
}
result.page = 'item';
result.title += ` | ${res.locals.title}`;
res.display('item', result);
}).catch(next);
};
... ...
... ... @@ -17,7 +17,7 @@ const list = {
// 列表页
index: (req, res, next) => {
let q = req.query;
let channel = req.cookies._Channel || 'men';
let channel = req.cookies._Channel || req.query._Channel || 'men';
q.page = parseInt(q.page || 1, 10);
q.order = q.order || 's_n_desc';
... ... @@ -69,9 +69,11 @@ const list = {
// 新品到着
newPage: (req, res, next) => {
let q = req.query;
let channel = req.cookies._Channel || req.query._Channel || 'men';
q.page = parseInt(q.page || 1, 10);
q.order = q.order || 's_n_desc';
q.channel = channel;
let retData = {
module: 'product',
... ... @@ -79,44 +81,45 @@ const list = {
title: '新品'
};
Promise.all([Resouces.newProductBanner(), Search.queryAllSort(), Search.queryNewProduct(q)]).then(result => {
let banner = result[0];
let sortData = camelCase(result[1]);
let listData = result[2];
let nav = [DataHelper.getChannelNav(), {
name: '新品'
}];
retData.banner = banner;
if (listData && listData.code === 200 && listData.data) {
let data = camelCase(listData.data);
if (data.filter) {
data.filter.groupSort = DataHelper.sortConvert(sortData.data.sort);
retData.filter = DataHelper.filterHandle(data.filter, q);
retData.filter.showPrice = data.total > 10;
Promise.all([Resouces.newProductBanner(channel), Search.queryAllSort(),
Search.queryNewProduct(q)]).then(result => {
let banner = result[0];
let sortData = camelCase(result[1]);
let listData = result[2];
let nav = [DataHelper.getChannelNav(), {
name: '新品'
}];
retData.banner = banner;
if (listData && listData.code === 200 && listData.data) {
let data = camelCase(listData.data);
if (data.filter) {
data.filter.groupSort = DataHelper.sortConvert(sortData.data.sort);
retData.filter = DataHelper.filterHandle(data.filter, q);
retData.filter.showPrice = data.total > 10;
}
retData.navPath = {
nav: nav
};
retData.paginationData = {
page: q.page,
limit: data.limit || 45,
total: data.total,
pageTotal: data.pageTotal,
queryParams: q
};
res.display('newList', _.assign(retData, {
products: DataHelper.handleProductList(data.productList, q),
order: q.order
}));
} else {
return Promise.reject('query product error');
}
retData.navPath = {
nav: nav
};
retData.paginationData = {
page: q.page,
limit: data.limit || 45,
total: data.total,
pageTotal: data.pageTotal,
queryParams: q
};
res.display('newList', _.assign(retData, {
products: DataHelper.handleProductList(data.productList, q),
order: q.order
}));
} else {
return Promise.reject('query product error');
}
}).catch(next);
}).catch(next);
}
};
... ...
... ... @@ -61,6 +61,10 @@ const shop = {
data.showEditorial = editorial && editorial.length === 3;
data.editorial = editorial;
_.forEach(editorial, (e) => {
e.title = e.title.length > 40 ? (e.title.substring(0, 40) + '...') : e.title;
});
if (productData && productData.code === 200 && productData.data) {
let ret = camelCase(productData.data);
... ... @@ -89,9 +93,9 @@ const shop = {
};
let nav = [DataHelper.getChannelNav()];
let domain = req.params.domain;
let uid = req.user.uid;
let q = req.query;
let hasBrand = !!q.brand;
q.order = q.order || 's_n_desc';
q.page = parseInt(q.page || 1, 10);
... ... @@ -125,9 +129,11 @@ const shop = {
let ret = camelCase(result.data);
if (ret.filter) {
delete q.brand;
if (!hasBrand) {
delete q.brand;
}
ret.filter.groupSort = DataHelper.sortConvert(allSort.data.sort);
data.filter = DataHelper.filterHandle(ret.filter, req.query);
data.filter = DataHelper.filterHandle(ret.filter, q);
data.filter.showPrice = ret.total > 10;
}
... ...
... ... @@ -87,15 +87,24 @@ const helpers = {
* 男女条件
* @returns {*[]}
*/
genders() {
return [
{
name: '男士',
value: '1,3'
}, {
name: '女士',
value: '2,3'
}];
genders(gender) {
if (gender) {
return Object.keys(gender).map(g => {
return {
name: gender[g],
value: g
};
});
} else {
return [
{
name: '男士',
value: '1,3'
}, {
name: '女士',
value: '2,3'
}];
}
},
/**
... ... @@ -216,7 +225,7 @@ const helpers = {
filterHandle(filter, q) {
let priceRange = filter.priceRange;
let sizeInfo = filter.size;
let genders = this.genders();
let genders = this.genders(filter.gender);
let brands = filter.brand;
let colors = this.colorConvert(filter.color);
let sorts = filter.groupSort;
... ... @@ -233,20 +242,36 @@ const helpers = {
}
});
if (sorts && sorts.length === 1) {
singleSort = true;
if (sorts[0].sub && sorts[0].sub.length === 1) {
sorts[0].sub[0].checked = true;
}
if (q.misort) {
sorts[0].sub.forEach(s => {
s.checked = s.categoryId === q.misort;
if (sorts) {
// singleSort = true;
//
// if (sorts[0].sub && sorts[0].sub.length === 1) {
// sorts[0].sub[0].checked = true;
// }
//
// if (q.misort) {
// sorts[0].sub.forEach(s => {
// s.checked = s.categoryId === q.misort;
// });
// }
//
// sorts = sorts[0].sub;
// } else if (sorts && sorts.length > 1) {
_.forEach(sorts, s => {
s.sub.unshift({
categoryName: '全部' + s.categoryName,
categoryId: '',
checked: q.msort && q.msort === s.categoryId && !q.misort
});
}
sorts = sorts[0].sub;
_.forEach(s.sub, ss => {
if (q.misort && q.misort === ss.categoryId) {
ss.checked = true;
s.checked = true;
}
});
});
}
let matchPrice = false;
... ... @@ -307,7 +332,7 @@ const helpers = {
return (',' + q.brand + ',').indexOf(',' + b.id + ',') >= 0;
}).map(b => {
b.checked = true;
return b.brandName;
return b.brandNameEn || b.brandName;
}).join('、');
if (brandNames) {
... ...
... ... @@ -93,10 +93,27 @@ const getModelTryAsync = skn => {
});
};
/**
* 获取品牌Banner API
* @function getBrandBannerAsync
* @param { number } brandId 品牌id
* @return { Object } banner信息
*/
const getBrandBannerAsync = brandId => {
return api.get('', {
method: 'web.brand.banner',
brand_id: brandId
}, {
cache: true,
code: 200
});
};
module.exports = {
getProductBaseAsync, // 获取商品基本信息
getUserIsFav, // 获取商品用户收藏信息
getsizeInfoAsync, // 获取商品尺码信息
getComfortAsync, // 获取商品材质信息
getModelTryAsync // 获取商品模特试穿信息
getModelTryAsync, // 获取商品模特试穿信息
getBrandBannerAsync // 获取品牌Banner信息
};
... ...
... ... @@ -9,6 +9,23 @@ const _ = require('lodash');
const helpers = global.yoho.helpers;
/**
* 获取原图片路径
* @function _getForceSourceUrl
* @param { String } url 图片路径
* @return { String } 原图片路径
*/
const _getForceSourceUrl = (url) => {
const str = ['?imageView', '?imageMogr2'];
_.forEach(str, value => {
url = _.split(url, value, 1)[0];
});
url.replace('http:', '');
return url;
};
/**
* 使sizeBoList id以 sizeAttributeBos id顺序一样
* @function _sizeInfoBoSort
* @param { Object } sizeInfoBo 尺码数据对象
... ... @@ -152,7 +169,7 @@ const setSeoInfo = (goodInfo, nav) => {
sortName = _.get(nav, '[1].name', '');
return {
title: `${brandName} ${sortName}|${goodInfo.name}正品`,
title: `${brandName} ${sortName} | ${goodInfo.name}正品`,
keywords: `${brandName} ${sortName},${brandName}正品官网专卖店,${brandName}官方授权店,${brandName}正品,` +
`${brandName}打折,${brandName}折扣店,${brandName}真品,${brandName}代购`,
description: _.get(goodInfo, 'share.shareDesc', goodInfo.name)
... ... @@ -232,22 +249,64 @@ const setPathNav = (data, name, channel) => {
* @param { Object } brand 品牌相关数据
* @return { Object } 品牌banner
*/
const setBrandBanner = brand => {
let data = {
brandId: brand.id,
bgColor: '#000',
brandLogo: {
link: `/product/shop/${brand.brandDomain}`, // 品牌跳转链接 -- 待处理
img: brand.brandIco
},
brandHome: {
link: `/product/shop/${brand.brandDomain}` // 品牌跳转链接 -- 待处理
const setBrandBanner = (base, brand, shop) => {
let resData = {},
banner = {};
if (base.brand) {
let info = base.brand;
// 基础品牌数据
banner = {
brandId: info.id,
bgColor: '#000',
brandLogo: {
link: `/product/shop/${info.brandDomain}`, // 品牌跳转链接
img: info.brandIco
},
brandHome: {
link: `/product/shop/${info.brandDomain}` // 品牌跳转链接
}
};
// 品牌banner数据
if (brand) {
if (brand.colorValue) {
banner.bgColor = brand.colorValue;
}
if (brand.bannerUrl) {
banner.bgImg = brand.bannerUrl;
}
if (brand.logo) {
_.set(banner, 'brandLogo.img', _getForceSourceUrl(brand.logo));
}
}
};
}
return {
brandBanner: data
};
switch (_.toNumber(shop.type)) {
case 1:
// 多品店不显示
banner = {};
break;
case 2:
// 单品店显示新版的店铺banner
_.forEach(shop.list, value => {
if (value.resource_name === 'shopTopBanner') {
banner.bgImg = _.get(JSON.parse(value.resource_data), 'detailSrc', false);
}
});
break;
default:
break;
}
if (!_.isEmpty(banner)) {
resData.brandBanner = banner;
}
return resData;
};
/**
... ...
... ... @@ -8,6 +8,7 @@
const _ = require('lodash');
const itemApi = require('./item-api');
const brandApi = require('./brand-api');
const shopApi = require('./shop-api');
const itemFun = require('./item-handler');
const search = require('./search-api');
... ... @@ -20,31 +21,55 @@ const search = require('./search-api');
* @return { Object } 返回单个商品品牌、材质、尺码、描述、详情等信息
*/
const _getMultiResourceByBaseInfo = (base) => {
let productId = base.id;
let skn = base.erpProductId;
let brandId = base.brand.id ? base.brand.id : 0;
const productId = base.id;
const skn = base.erpProductId;
const brandId = base.brandId || 0;
const brandDomain = _.get(base, 'brand.brandDomain', false);
const shopId = base.shopId || 0;
let apiIndex = {};
// 获取相关数据
let promiseData = [
itemApi.getsizeInfoAsync(skn),
itemApi.getComfortAsync(productId),
itemApi.getModelTryAsync(skn),
search.getSortAsync({sort: base.smallSortId})
search.getSortAsync({sort: base.smallSortId}),
itemApi.getBrandBannerAsync(brandId)
];
if (base.uid) {
apiIndex.productFav = promiseData.length;
promiseData.push(itemApi.getUserIsFav(base.uid, productId));
apiIndex.brandFav = promiseData.length;
promiseData.push(brandApi.getBrandInfo(brandId, base.uid));
}
// 根据品牌名获取店铺信息
if (brandDomain) {
apiIndex.domainInfo = promiseData.length;
promiseData.push(brandApi.getDomainInfo(brandDomain));
}
// 根据店铺ID获取店铺装修信息
if (shopId) {
apiIndex.shopDecorator = promiseData.length;
promiseData.push(shopApi.getShopDecorator(shopId));
}
return Promise.all(promiseData).then(result => {
return {
sizeInfo: result[0],
comfort: result[1].data,
modelTry: result[2].data,
sort: result[3].data,
productFav: (result[4] && result[4].data),
brandFav: (result[5] && result[5].data && result[5].data.is_favorite === 'Y')
brandBanner: result[4].data,
productFav: (result[apiIndex.productFav] && result[apiIndex.productFav].data),
brandFav: (_.get(result[apiIndex.brandFav], 'data.is_favorite', 'N') === 'Y'),
shopInfo: Object.assign(
{},
{type: _.get(result[apiIndex.domainInfo], 'data.type', 0)},
{list: _.get(result[apiIndex.shopDecorator], 'data.list', [])}
)
};
});
};
... ... @@ -78,27 +103,28 @@ const getProductItemData = (params, url, uid) => {
// 商品基本信息
data.goodInfo = itemFun.setProductData(result);
// BRAND品牌简介
if (result.brand) {
Object.assign(
data,
itemFun.setBrandBanner(result.brand), // banner
itemFun.setBrandIntro(result.brand) // BRAND品牌简介
);
Object.assign(data, itemFun.setBrandIntro(result.brand));
}
return _getMultiResourceByBaseInfo(result).then(mulRes => {
// 收藏状态
_.set(data, 'brandBanner.brandFav', mulRes.brandFav);
_.set(data, 'goodInfo.productFav', mulRes.productFav);
Object.assign(data,
itemFun.setBrandBanner(result, mulRes.brandBanner, mulRes.shopInfo), // banner
itemFun.setPathNav(mulRes.sort, result.productName, params.channel), // 面包屑导航
itemFun.setDescriptionData(mulRes.sizeInfo, mulRes.comfort), // DESCRIPTION商品描述
itemFun.setMaterialData(mulRes.sizeInfo), // MATERIALS材料洗涤
itemFun.setSizeData(mulRes.sizeInfo, mulRes.modelTry), // SIZEINFO尺码信息
itemFun.setDetailData(mulRes.sizeInfo) // DETAILS商品详情
);
// 收藏状态
_.set(data, 'goodInfo.productFav', mulRes.productFav);
if (data.brandBanner) {
_.set(data, 'brandBanner.brandFav', mulRes.brandFav);
}
resData.content = data;
// 商品详情SEO
... ... @@ -114,11 +140,11 @@ const getProductItemData = (params, url, uid) => {
* 获取商品尺寸,颜色,和缩略图
* @function getProductInfo
* @param { Number } productId 商品ID
* @param { String } uid 用户ID
* @param { Number } skn 商品skn
* @param { String } uid 用户ID
* @return { Object } 接口返回单个商品的基本信息
*/
const getProductInfo = (productId, uid, skn) => {
const getProductInfo = (productId, skn, uid) => {
return itemApi.getProductBaseAsync(productId, uid, skn).then(result => {
return itemFun.setProductData(result);
});
... ...
... ... @@ -14,10 +14,16 @@ const Resources = {
* 获取新品到着-列表页-banner 资源位
* @returns {Promise.<T>}
*/
newProductBanner() {
newProductBanner(channel) {
let codes = {
men: '3d3127b220073fbcda738c8ba26561e5',
women: '2aaf9519b31d1f21c537032f496e7450',
lifestyle: '422bfdb9b410598e11c3e72bd4ee664e'
};
let params = {
content_code: 'a7989369aa86681c678bc40f171b8f1d'
content_code: codes[channel]
};
return api.get('/operations/api/v5/resource/get', params).then(result => {
... ...
... ... @@ -12,6 +12,7 @@ const co = Promise.coroutine;
const camelCase = global.yoho.camelCase;
const BrandService = require('./brand-service');
const ShopApi = require('./shop-api');
const Search = require('../models/search');
const _ = require('lodash');
/**
... ... @@ -90,19 +91,27 @@ const ShopService = {
* 获取店铺二级分类
* @param shopId
*/
getShopSecondSorts(shopId) {
getShopSecondSorts(brandId, shopId) {
return co(function*() {
let data = yield ShopApi.getShopSorts(shopId);
if (data && data.code === 200) {
let sorts = camelCase(data.data);
let data = yield Search.queryAllSort({
brand: brandId,
shop: shopId,
small_sort: 0
});
let sortArray = [];
if (data && data.data) {
let sorts = camelCase(data.data.sort);
sorts.forEach(s => {
sortArray = sortArray.concat(s.sub);
});
return sorts.sort((a, b) => {
return a.sub.length >= b.sub.length;
sortArray = sortArray.sort((s1, s2) => {
return s2.count - s1.count;
});
} else {
return [];
}
return sortArray;
})();
},
... ... @@ -130,7 +139,7 @@ const ShopService = {
info.isFavorite = shopIntro.isFavorite === 'Y';
let shopData = yield Promise.all([ShopService.getShopDecorator(shopId),
ShopService.getShopSecondSorts(shopId)]);
ShopService.getShopSecondSorts(domainInfo.id, shopId)]);
let shopList = shopData[0];
let sorts = shopData[1];
let resources = resourceDataHandle(shopList.list);
... ...
... ... @@ -9,6 +9,13 @@
{{# goodInfo}}
<div class="product-main clearfix" data-id="{{id}}">
<div class="thumbs left clearfix">
<div class="thumb-show right">
<img id="main-thumb" src="{{image img 482 643}}" style="display: block;">
<div class="check-btns">
<span class="iconfont pre-thumb">&#xe62c;</span>
<span class="iconfont next-thumb">&#xe629;</span>
</div>
</div>
<div class="thumb-list hide">
{{# colors}}
<div class="thumb-wrap{{#unless cur}} hide{{/unless}}">
... ... @@ -24,13 +31,6 @@
</div>
{{/ colors}}
</div>
<div class="thumb-show right">
<img id="main-thumb" src="{{image img 482 643}}" style="display: block;">
<div class="check-btns">
<span class="iconfont pre-thumb">&#xe62c;</span>
<span class="iconfont next-thumb">&#xe629;</span>
</div>
</div>
</div>
<div class="infos left clearfix">
<p class="brand-name">{{brandName}}</p>
... ...
... ... @@ -16,10 +16,10 @@
</div>
<div class="right">
{{!-- 新品banner --}}
{{# banner}}
{{#if banner.src}}
<div class="banner-img"
style="height: 200px;margin-bottom:30px; background:url({{image src 850 200 2}}) no-repeat top center;"></div>
{{/banner}}
style="height: 200px;margin-bottom:30px; background:url({{image banner.src 850 200 2}}) no-repeat top center;"></div>
{{/if}}
{{!-- 已选中条件 --}}
{{#filter}}
{{> list/filter-area}}
... ...
... ... @@ -36,7 +36,7 @@
<div class="floor-header clearfix">
<h2 class="floor-title en-size">NEW ARRIVALS</h2>
<p class="floor-title zh-size">最新上架</p>
<a class="floor-more" href="{{@root.mores.new.href}}">MORE</a>
<a class="floor-more" href="{{@root.mores.new.url}}">MORE</a>
</div>
<div class="goods-wrap">
... ... @@ -60,7 +60,7 @@
<div class="floor-header clearfix">
<h2 class="floor-title en-size">HOT</h2>
<p class="floor-title zh-size">人气单品</p>
<a class="floor-more" href="{{@root.mores.hot.href}}">MORE</a>
<a class="floor-more" href="{{@root.mores.hot.url}}">MORE</a>
</div>
<div class="goods-wrap">
... ... @@ -69,6 +69,7 @@
<a href="/product/pro_{{productId}}_{{goodsId}}/{{cnAlphabet}}.html" target="_blank">
<img class="lazy thumb" data-original="{{src}}" style="display: block;">
</a>
<div class="desc-cover"></div>
<div class="desc">
<a class="name" href="" target="_blank">{{productName}}</a>
<p class="price">¥{{round salesPrice 2}}</p>
... ... @@ -119,7 +120,6 @@
</a>
<div class="desc">
<a class="name" href="{{url}}" target="_blank">{{title}}</a>
<p class="intro">{{intro}}</p>
</div>
</div>
{{/each}}
... ...
... ... @@ -5,7 +5,7 @@
<div class="center-content clearfix">
{{# brandLogo}}
<a href="{{link}}" class="brand-logo">
<img src="{{image img 108 44}}">
<img src="{{image img 108 45}}">
</a>
{{/ brandLogo}}
<p class="opt right">
... ...
... ... @@ -74,7 +74,7 @@
<div class="brand-list nano">
<div class="nano-content">
{{#each brandData}}
<div class="input-radio" data-value="{{id}}">
<div class="input-radio {{#if checked}}default-check{{/if}}" data-value="{{id}}" data-word="{{brandAlif}}">
{{> icon/radio}}
{{#if brandNameEn}}
<label>{{brandNameEn}}</label>
... ...
<div class="shop-sort">
<div class="all">所有商品</div>
<div class="sort-list">
<div><a href="{{@root.mores.all.href}}?gender=2,3">女装</a></div>
<div><a href="{{@root.mores.all.href}}?gender=1,3">男装</a></div>
<div><a href="{{@root.mores.all.url}}?gender=2,3">女装</a></div>
<div><a href="{{@root.mores.all.url}}?gender=1,3">男装</a></div>
{{# each banner.sorts}}
<div><a href="{{@root.mores.all.href}}?sort={{relationParameter.sort}}">{{categoryName}}</a></div>
<div><a href="{{@root.mores.all.url}}?misort={{sortId}}">{{sortName}}</a></div>
{{/ each}}
</div>
<div class="more"><a href="{{@root.mores.all.href}}">MORE</a></div>
<div class="more"><a href="{{@root.mores.all.url}}">MORE</a></div>
</div>
... ...
... ... @@ -79,13 +79,13 @@ const callback = (req, res) => {
if (result.code === 200) {
let data = result.data;
res.render('pay-success', {
res.display('pay-success', {
defaultHeader: false,
content: {
cost: data.pay,
orderNum: data.orderCode,
onlineCost: data.pay,
orderHref: helpers.urlFormat('/me/order/detail', {code: data.orderCode}),
orderHref: helpers.urlFormat('/me/order/detail', {orderCode: data.orderCode}),
walkHref: helpers.urlFormat('/')
}
});
... ...
... ... @@ -44,11 +44,11 @@ exports.lte = (num1, num2, options) => {
* @return {[boolen]}
*/
exports.showStorage = (leftNumber) => {
leftNumber = typeof num1 === 'number' ? leftNumber : parseFloat(leftNumber, 10);
leftNumber = +leftNumber;
if (leftNumber <= 3 && leftNumber >= 0) {
if (leftNumber <= 3 && leftNumber > 0) {
return `仅剩${leftNumber}件`;
} else if (leftNumber < 0) {
} else if (leftNumber <= 0) {
return '库存不足';
}
};
... ...
... ... @@ -18,7 +18,7 @@ const pay = require(`${cRoot}/pay`); // 支付
router.get('/cart', cartCtrl.index);
router.post('/cart/product/change_num', cartCtrl.changeProductNum);
router.delete('/cart/product/remove', cartCtrl.removeProduct);
router.post('/cart/product/send_to_favorite', cartCtrl.sendToFavorite);
router.post('/cart/product/send_to_favorite', auth, cartCtrl.sendToFavorite);
router.post('/cart/add', cartCtrl.addToCart);
router.post('/cart/toggleSelectGoods', cartCtrl.toggleSelectGoods);
router.get('/cart/checkStorage', cartCtrl.checkStorage);
... ...
... ... @@ -21,7 +21,7 @@
1.每天15:00以前成功支付的订单将在当天发货,15:00-00:00成功付款的订单将在第二天发货。
2.当订单发货后,您可以登录<a class="blue" href="/me/order">订单中心</a>查询快递发货详情。
3.YOHO!BLK有货支持“开箱验货”和“15天退换货保障”收货后请当面验货,如果发现商品有任何问题请致电客服电话400-889-9646,
<a class="blue" href="">“退换货政策”</a>请点击查看。<em class="blue">4.尊敬的用户:近期为网络诈骗高发期,YOHO!BLK有货郑重声明,不会以任何形式索取客户的账户信息或引导转账,敬请提高警惕,谨防诈骗</em>
<a class="blue" href="/help?id=43">“退换货政策”</a>请点击查看。<em class="blue">4.尊敬的用户:近期为网络诈骗高发期,YOHO!BLK有货郑重声明,不会以任何形式索取客户的账户信息或引导转账,敬请提高警惕,谨防诈骗</em>
</p>
</div>
{{/ content}}
... ...
... ... @@ -33,9 +33,9 @@
{{/if}}
<span class="iconfont">&#xe63c;</span>
</div>
{{#expect_arrival_time}}
{{#if expect_arrival_time}}
<div class="published-at">上市期: {{expect_arrival_time}}</div>
{{/expect_arrival_time}}
{{/if}}
</li>
<li class="price-num">
<span class="price sale-price">¥ {{round sales_price 2}}</span>
... ...
... ... @@ -8,7 +8,7 @@
</label>
</div>
<div class="item product">货品</div>
<div class="item price">价格</div>
<div class="item price">单价</div>
<div class="item num">数量</div>
<div class="item pro-total-price">总价</div>
<div class="item actions">操作</div>
... ...
... ... @@ -7,7 +7,7 @@
</p>
{{^}}
<p class="info-text">
温馨提示: 亲爱的顾客,您还没有<a href="/passport/login">登录</a>哦, 所有的商品价、活动信息以登录后显示为准。
温馨提示: 亲爱的顾客,您还没有<a href="/passport/login">登录</a>哦, 所有的商品价、活动信息以登录后显示为准。
<span class="iconfont pull-right close">&#xe608;</span>
</p>
{{/if}}
... ...
... ... @@ -12,11 +12,11 @@ const channelMap = {
gender: '1,3'
},
women: {
code: '527079e6c46d0f125eb46b835968971b',
code: 'a3a3901627d22fbebc4e74360ed2dacc',
gender: '2,3'
},
lifestyle: {
code: '94b5ed607b6d565ffc29c2c04be121dc',
code: '2cff34ee8fc290adad8c2029d497eede',
gender: ''
}
};
... ...
... ... @@ -22,7 +22,7 @@ exports.notFound = () => {
});
}
return res.display('error/404', {
return res.status(404).display('error/404', {
module: 'common',
page: 'error',
title: '页面不存在 | Yoho!BLK | 潮流购物逛不停'
... ... @@ -50,7 +50,7 @@ exports.serverError = () => {
}
renderErrPage = (result) => {
res.display('error/500', {
res.status(500).display('error/500', {
module: 'common',
page: 'error',
err: err,
... ...
... ... @@ -84,7 +84,7 @@ const getBrandItems = (data) => {
_.forEach(data, item => {
brandItems.push({
link: item.sort_url,
hot: item.is_hot === 'Y' ? true : false,
hot: item.is_hot === 'Y',
name: item.sort_name
});
});
... ... @@ -103,16 +103,15 @@ const getThirdNav = (data) => {
_.forEach(data, item => {
let obj = {
link: item.sort_url,
name: item.sort_name
hot: item.is_hot === 'Y',
name: item.sort_name,
category: true
};
thirdNav.push(obj);
if (item.sub) {
thirdNav = _.concat(thirdNav, getBrandItems(item.sub));
obj.category = true;
// obj.brandItems = getBrandItems(item.sub);
}
});
... ... @@ -183,6 +182,9 @@ const requestNavBar = (type) => {
cache: true,
code: 200
}).then(res => {
if (!res) {
return {};
}
return setHeaderData(res.data, type);
});
};
... ...
... ... @@ -10,6 +10,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta content="telephone=no" name="format-detection" />
<meta content="email=no" name="format-detection" />
<meta name="renderer" content="webkit">
<link rel="dns-prefetch" href="//cdn.yoho.cn">
<link rel="dns-prefetch" href="//static.yohobuy.com">
<link rel="dns-prefetch" href="//img12.static.yhbimg.com">
... ...
... ... @@ -5,7 +5,7 @@
<ul class="main-nav-list">
{{# navbars}}
<li class="nav-item{{#if cur}} cur{{/if}}">
<a href="{{link}}">
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}>
<span class="nav-en">{{en}}</span>
<span class="nav-cn">{{cn}}</span>
</a>
... ... @@ -35,6 +35,9 @@
{{#if category}}
<dt>
<a href="{{link}}">{{name}}</a>
{{#if hot}}
<span class="hot"></span>
{{/if}}
</dt>
{{^}}
<dd>
... ...
... ... @@ -4,11 +4,11 @@
* @return {[boolen]}
*/
module.exports = function(leftNumber) {
leftNumber = typeof num1 === 'number' ? leftNumber : parseFloat(leftNumber, 10);
leftNumber = +leftNumber;
if (leftNumber <= 3 && leftNumber >= 0) {
return `仅剩${leftNumber}件`;
} else if (leftNumber < 0) {
if (leftNumber <= 3 && leftNumber > 0) {
return '仅剩' + leftNumber + '件';
} else if (leftNumber <= 0) {
return '库存不足';
}
};
... ...
... ... @@ -20,13 +20,26 @@ lazyLoad($('.brand-page img.lazy'));
// 品牌类别滚动事件
$(window).scroll(function() {
if ($(this).scrollTop() >= categoryTop) {
$gory.addClass('category-fix');
} else {
$gory.removeClass('category-fix');
}
});
$('.brand-key').each(function(i, ele) {
var key;
if ($(ele).parent().attr('style')) {
return;
}
if ($(ele).offset().top - $(window).scrollTop() < 100) {
key = $(ele).html().charAt($(ele).html().length - 1);
$('.category-nav').find('.' + key).css('color', '#379ed6').siblings().css('color', '#222');
}
});
});
// 点击字母,页面滚动到相关区域
$category.click(function() {
... ...
... ... @@ -17,6 +17,19 @@ function showOrNot() {
}
}
function reposReturnTop() {
var $top = $returnTop.parent();
if (!$top.hasClass('service-top')) {
$top = $returnTop;
}
if ($(window).width() < 1380) {
$top.addClass('for-min');
} else {
$top.removeClass('for-min');
}
}
$returnTop.click(function() {
$('html,body').animate({
scrollTop: 0
... ... @@ -36,4 +49,10 @@ if ($returnTop.hasClass('hide')) {
$('img').load(showOrNot);
}
reposReturnTop();
if ($returnTop.length) {
$(window).resize(reposReturnTop);
}
exports.returnTopShowOrNot = showOrNot;
... ...
... ... @@ -17,10 +17,14 @@ var $commentList = $commentArea.find('.comments-wrap'),
MAX_COMMENTS_WORDS = 100,
$wordCountTip = $('#word-count-tip'),
_alert = dialog.Alert,
tag = $('#tags').find('li').length;
tag = $('#tags').find('li').length,
leng = $('.goods').find('.good-info').length / 4,
pag = 0;
require('../plugins/share');
// require('yoho-unslider');
$('#prise-btn').click(function() {
var prising = false,
url,
... ... @@ -192,4 +196,37 @@ if ($('.collocation').find('.good-info').length === 0) {
if ($('.goods').find('.good-info').length === 0) {
$('.related-group').hide();
} else if ($('.goods').find('.good-info').length < 5) {
$('.left').hide();
$('.right').hide();
}
$('.right').click(function() {
pag = pag + 1;
if (pag === leng - 1) {
$('.right').addClass('hide');
}
if (pag !== 0) {
$('.left').removeClass('hide');
}
$('.goods').animate({left: '+=-930px'});
});
$('.left').click(function() {
var $goods = $('.goods');
pag = pag - 1;
if (pag === 0) {
$('.left').addClass('hide');
}
if (pag !== leng - 1) {
$('.right').removeClass('hide');
}
$goods.animate({left: '+=930px'});
});
... ...
... ... @@ -25,11 +25,11 @@ require('../common/return-top'); // return-top
lazyLoad($('.banner-img'));
// 浮动在线客服和返回顶部的鼠标移入移出切换效果
$('.service, .return-top').hover(function() {
$(this).find('.iconfont').addClass('hide').end().find('.hover-text').removeClass('hide');
}, function() {
$(this).find('.iconfont').removeClass('hide').end().find('.hover-text').addClass('hide');
});
function toggleShow(e) {
$(e.target).find('span').toggleClass('hide');
}
$('.service, .return-top').hover(toggleShow, toggleShow);
// repos service-return when window resize
$(window).resize(reposServiceTop);
... ...
... ... @@ -19,7 +19,6 @@ var confirmReceive = require('./order/confirm-receive');
// 订单剩余时间显示及倒计时
var countDown = require('./order/countdown');
require('../common/foreach-polyfill');
// 更新表格
var tableOperation = {
... ... @@ -41,6 +40,8 @@ var typeMap = {
delivering: 3
};
require('../common/foreach-polyfill');
// 个人中心共用代码加载
require('./me');
... ... @@ -150,9 +151,9 @@ function updateTableContent($el) {
// 绑定分页点击事件
function bindPaginationClick() {
$('.blk-pagination li').off('click').on('click', function(e) {
$('.blk-pagination a').off('click').on('click', function(e) {
var $this = $(this);
var page = $this.find('a').attr('href').split('=')[1];
var page = $this.attr('href').split('=')[1];
var type = getCurrentTabType();
e.preventDefault();
... ...
var _alert = require('../plugins/dialog').Alert;
var lazyLoad = require('yoho-jquery-lazyload');
lazyLoad($('img.banner-img'));
$('.cancel-apply').on('click', function() {
var id = $(this).data('applyId');
... ...
... ... @@ -24,7 +24,7 @@ var jQuery = require('yoho-jquery');
init: function(options) {
var type = options.type || defaults.type;
var ele = this;
var _checked = $('.iconfont', this).hasClass('checked');
var _checked = options.checked || $('.iconfont', this).hasClass('checked');
options = $.extend({}, defaults, $(this).data('options'), defaultsHtml[type], options);
... ...
... ... @@ -60,6 +60,7 @@ var YohoListPage = {
YohoListPage.go({
msort: categoryId,
misort: subCategoryId,
size: '',
page: 1
});
}
... ... @@ -301,6 +302,11 @@ var YohoListPage = {
$('.input-radio', this.brandsDoc).check({
type: 'radio'
});
$('.input-radio.default-check', this.brandsDoc).check({
type: 'radio',
checked: true
});
},
openStyleMulit: function() {
... ... @@ -332,16 +338,18 @@ var YohoListPage = {
},
filterBrand: function(letter) {
$('.yoho-product-list .brand-list .input-radio').each(function() {
if (letter === '0-9') {
var first = $('label', this).text().toLowerCase().charAt(0); // eslint-disable-line
var first = $(this).data('word').toString(); // eslint-disable-line
first = first.toLowerCase();
if (letter === '0-9') {
console.log(first);
if ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')) {
$(this).hide();
} else {
$(this).show();
}
} else {
if ($('label', this).text().toLowerCase().indexOf(letter) === 0) {
if (!letter || first === letter) {
$(this).show();
} else {
$(this).hide();
... ...
... ... @@ -72,8 +72,8 @@ $(function() {
// 编辑商品颜色和属性
Cart.editColorOrSize(
$this.attr('data-productId'),
$this.attr('data-productSkn'),
$this.attr('data-productid'),
$this.attr('data-productskn'),
$this.find('.default-color').text(),
$this.find('.default-size').text(), function() {
editable = true;
... ... @@ -91,7 +91,7 @@ $(function() {
if ($('.chk-group').length) {
// 防止回退history.go(-1)时页面状态不改变
// 这里可以添加loading效果
window.location.reload();
// window.location.reload();
Cart.checkStorage(function() {
if (!$(this).hasClass('disable')) {
window.location.href = '/shopping/order';
... ...
... ... @@ -22,7 +22,19 @@ var removedProsInfo = [];
var removedGoodsTpl = require('../../../tpl/shopping/removed-goods.hbs');
var editTpl = require('../../../tpl/shopping/edit-color-size.hbs');
var Cart = {
var Cart;
require('yoho-jquery-dotdotdot');
function dotName() {
// product name dotdotdot
$('.pro-name a').dotdotdot({
wrap: 'letter'
});
}
Cart = {
/*
* 添加到购物车
* @function [addToCart]
... ... @@ -111,6 +123,7 @@ var Cart = {
success: function(res) {
Util.refreshCart(res, function() {
Stepper.init();
if (callback) {
return callback();
}
... ... @@ -298,6 +311,7 @@ var Cart = {
success: function(res) {
Util.refreshCart(res, function() {
Stepper.init();
if (callback) {
return callback();
}
... ... @@ -445,6 +459,9 @@ var Cart = {
},
fail: function() {
new _alert('此商品无法编辑颜色和尺寸').show();
},
complete: function() {
setEditable();
}
});
... ... @@ -471,4 +488,6 @@ var Cart = {
}
};
dotName();
module.exports = Cart;
... ...
.brand-img-box {
width: 566px;
width: 565px;
box-sizing: border-box;
display: inline-block;
... ... @@ -17,14 +17,14 @@
.big-img {
width: 100%;
height: 333px;
height: 340px;
}
.small-img {
width: 50%;
height: 283px;
width: 281px;
height: 285px;
display: inline-block;
box-sizing: border-box;
box-sizing: content-box;
border: 1px solid $grayBorder;
border-top: none;
... ...
... ... @@ -11,8 +11,15 @@
position: relative;
h4 {
height: 18px;
line-height: 18px;
padding-right: 10px;
display: -webkit-box;
font-size: 16px;
font-weight: bold;
overflow: hidden;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
p {
... ...
.editorial-container {
$bigImgWidth: 395px;
$smallImgWidth: 360px;
$smallImgHeight: 240px;
$bigImgWidth: 390px;
$smallImgWidth: 358px;
$smallImgHeight: 243px;
$space: 10px;
@extend .resource-container;
... ... @@ -14,12 +14,28 @@
display: inline-block;
box-sizing: border-box;
.bottom-space {
margin-bottom: $space;
.box {
display: inline-block;
position: relative;
&.bottom-space {
margin-bottom: 14px;
}
&.right-space {
margin-right: 15px;
}
}
.right-space {
margin-right: $space;
.editorial-title {
width: 100%;
min-height: 50px;
padding: $space;
position: absolute;
bottom: 2px;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 14px;
}
img {
... ... @@ -33,7 +49,7 @@
img {
width: $bigImgWidth;
height: calc($smallImgHeight*2+$space+2px);
height: calc($smallImgHeight*2+$space+4px);
}
}
... ... @@ -41,7 +57,8 @@
width: calc($smallImgWidth*2+$space*2);
float: right;
img {
img,
.box {
width: $smallImgWidth;
height: $smallImgHeight;
}
... ...
... ... @@ -53,7 +53,7 @@
img {
width: 1150px;
height: auto;
height: $sliderHeight;
}
}
}
... ...
... ... @@ -12,7 +12,7 @@
}
.arrival-item {
width: 325px;
width: 327px;
display: inline-block;
.brand-name {
... ... @@ -48,7 +48,7 @@
&.normal {
float: left;
margin-right: 18px;
margin-right: 20px;
}
&.last {
... ... @@ -56,7 +56,7 @@
}
&.small-img {
width: 223px;
width: 218px;
}
}
}
... ...
... ... @@ -79,10 +79,9 @@
border: 1px solid #404040;
border-left: none;
* {
p,
h4 {
display: block;
}
p {
display: -webkit-box;
}
}
... ...
... ... @@ -63,7 +63,7 @@
.dist-name {
font-size: 12px;
display: inline-block;
width: 75px;
width: 66px;
padding: 3px;
text-overflow: ellipsis;
white-space: nowrap;
... ...
... ... @@ -42,8 +42,10 @@
color: #fff;
}
&.min {
margin-left: 505px;
&.for-min {
right: 20px;
left: auto;
margin-left: auto;
}
}
}
... ...
.article-title {
position: relative;
text-align: center;
margin-left: 30px;
width: 933px;
.title-line {
border-bottom: 1px solid #bbb;
... ...
.editorial-detail-page {
width: 940px;
width: 990px;
.detail-body {
width: 936px;
width: 990px;
margin: 0 auto;
overflow: hidden;
}
.detail-title {
... ... @@ -94,9 +93,65 @@
}
.related-reco {
width: 1000px;
width: 990px;
padding-top: 30px;
overflow: hidden;
position: relative;
.collocation {
margin-left: 30px;
}
.btn {
position: relative;
}
.left {
position: absolute;
display: block;
width: 30px;
height: 50px;
left: 0;
top: 200px;
color: #fff;
background: #ccc;
text-align: center;
line-height: 50px;
}
.left:hover {
background: #7f7f7f;
}
.right:hover {
background: #7f7f7f;
}
.right {
position: absolute;
display: block;
width: 30px;
height: 50px;
right: 0;
top: 200px;
color: #fff;
background: #ccc;
text-align: center;
line-height: 50px;
}
}
.related-group {
position: relative;
height: 450px;
}
.related-groups {
width: 930px;
overflow: hidden;
position: relative;
height: 420px;
margin-left: 30px;
}
.related-goods {
... ... @@ -105,7 +160,7 @@
.good-info {
width: 219px;
float: left;
margin: 0 20px 15px 0;
margin: 0 18px 15px 0;
img {
width: 219px;
... ... @@ -120,6 +175,7 @@
display: block;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.price {
... ... @@ -129,7 +185,16 @@
text-align: center;
}
}
}
.goods {
height: 420px;
overflow: hidden;
position: absolute;
.good-info:nth-child(4n) {
margin-right: 0;
}
}
.related-brand {
... ... @@ -138,8 +203,9 @@
.brands {
width: 950px;
padding: 30px 0;
padding: 30px 0 30px 30px;
overflow: hidden;
}
.brand {
... ... @@ -252,11 +318,12 @@
.article-tag {
float: left;
width: 100%;
width: 940px;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
padding: 20px 0;
padding: 20px 0 20px 10px;
overflow: hidden;
margin-left: 30px;
.tag-icon {
float: left;
... ... @@ -302,7 +369,8 @@
line-height: 25px;
border: 1px solid #fff;
overflow: hidden;
width: 100%;
width: 940px;
margin-left: 30px;
a {
color: #afafaf;
... ... @@ -319,6 +387,8 @@
.comment-area {
margin-top: 58px;
width: 935px;
margin-left: 30px;
.comment-publish {
height: 48px;
... ... @@ -448,23 +518,12 @@
}
}
.good-info {
width: 219px;
margin-right: 0;
float: left;
margin-right: 20px;
img {
width: 219px;
height: 295px;
}
.share {
padding-left: 30px;
width: 330px;
}
.name {
font-size: 12px;
line-height: 30px;
height: 30px;
overflow: hidden;
display: block;
}
.hide {
display: none;
}
}
... ...
... ... @@ -24,5 +24,4 @@
@import "msg";
@import "list";
@import "detail";
@import "related-goods";
@import "article-related";
... ...
.good-info {
width: 219px;
margin-right: 0;
float: left;
margin-right: 20px;
img {
width: 219px;
height: 295px;
}
.name {
font-size: 12px;
line-height: 30px;
height: 30px;
overflow: hidden;
display: block;
}
}
... ... @@ -14,6 +14,10 @@
margin: $space 0;
}
.no-pointer {
cursor: auto !important;
}
.refund-tag {
width: 55%;
padding: 5px 0;
... ... @@ -30,11 +34,14 @@
-webkit-box-orient: vertical;
}
.good-name-text:hover,
.check-detail:hover {
.good-name-text:hover {
text-decoration: underline;
}
.check-detail:hover {
@extend .blue;
}
.change-tag {
margin: 10px auto 0;
color: #1b1b1b;
... ... @@ -122,7 +129,7 @@
.iconfont {
font-weight: normal;
color: #000000;
color: #000;
}
&.last {
... ...
... ... @@ -2,18 +2,17 @@
position: relative;
.banner-img {
min-height: 44px;
min-height: 45px;
}
.opt-wrap {
width: 100%;
height: 44px;
height: 45px;
position: absolute;
top: 50%;
margin-top: -22px;
margin-top: -23px;
.brand-logo {
width: 108px;
display: inline-block;
}
... ...
... ... @@ -17,6 +17,9 @@
.input-radio {
padding: 10px 1px;
cursor: pointer;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.round-color {
width: 22px;
... ... @@ -41,11 +44,8 @@
font-size: 14px;
font-weight: 700;
margin-left: 10px;
display: inline-block;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.input-radio label.round-color {
margin-left: 0;
}
... ... @@ -369,6 +369,7 @@
.blk-pagination {
text-align: center;
margin: 20px 0 40px;
width: auto;
li {
width: auto;
... ...
... ... @@ -131,17 +131,30 @@
display: inline-block;
width: 369px;
height: 495px;
margin: 0 9px;
margin: 10px 9px;
border-width: 1px;
.desc {
img {
height: 495px;
}
.desc-cover {
position: absolute;
bottom: 0;
width: 100%;
background-color: #000;
opacity: 0.6;
padding: 20px 0;
opacity: 0.2;
height: 100px;
z-index: 1;
}
.desc {
position: absolute;
bottom: 0;
width: 100%;
padding: 17px 0;
color: #fff;
z-index: 2;
}
}
... ... @@ -169,18 +182,14 @@
border-width: 1px;
.desc {
margin-top: 20px;
margin-top: 10px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 25px;
}
.name,
.intro {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 10px 0;
}
}
... ...
... ... @@ -112,9 +112,6 @@ $hoverColor: #379ed6;
width: 234px;
word-wrap: break-word;
margin-bottom: 13px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 1.3em;
overflow: hidden;
:hover {
... ...