Authored by htoooth

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

/**
* 商品详情页controller
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2016/7/17
*/
'use strict';
const helpers = global.yoho.helpers;
const fav = require('../models/favorite');
const product = (req, res, next) => {
let uid = req.user.uid;
let pid = req.body.productId;
let type = (req.body.type === 'add');
let resData = {
code: 400,
message: '操作失败'
};
if (uid) {
if (pid) {
fav.toggleFavProduct(pid, uid, type).then(result => {
res.json(result);
}).catch(next);
return;
}
} else {
Object.assign(resData, {
code: 403,
message: '请登录后执行该操作',
data: {
refer: helpers.urlFormat('/signin')
}
});
}
res.json(resData);
};
module.exports = {
product // 组件demo页
};
... ...
... ... @@ -241,7 +241,7 @@ const index = (req, res, next) => {
// }
// };
Item.getProductItemData(req.params).then(result => {
Item.getProductItemData(req.params, req.user.uid).then(result => {
if (_.isEmpty(result)) {
return next();
}
... ...
... ... @@ -10,7 +10,7 @@ const list = {
index: (req, res, next) => {
let q = req.query;
q.page = q.page || 1;
q.page = parseInt(q.page || 1, 10);
let retData = {
module: 'product',
... ... @@ -54,7 +54,7 @@ const list = {
newPage: (req, res, next) => {
let q = req.query;
q.page = q.page || 1;
q.page = parseInt(q.page || 1, 10);
let retData = {
module: 'product',
... ...
... ... @@ -17,7 +17,7 @@ const Query = {
index: (req, res, next) => {
let q = req.query;
q.page = q.page || 1;
q.page = parseInt(q.page || 1, 10);
let retData = {
module: 'product',
... ...
... ... @@ -57,11 +57,16 @@ const shop = {
let uid = cookie.getUid(req);
let q = req.query;
q.page = q.page || 1;
q.page = parseInt(q.page || 1, 10);
ShopData.getShopHeadData(domain, uid).then(result => {
data.banner = result;
if (data.banner.banner) {
data.banner.banner = data.banner.banner.split('?')[0];
}
if (result.brandId) {
q.brand = result.brandId;
q.shop_id = result.shopId;
... ...
/**
* 收藏相关接口
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2016/7/17
*/
'use strict';
const api = global.yoho.API;
const addFavAsync = (uid, pid) => {
return api.get('', {
method: 'app.favorite.add',
id: pid,
uid: uid,
type: 'product'
});
};
const cancelFavAsync = (uid, pid) => {
return api.get('', {
method: 'app.favorite.cancel',
fav_id: pid,
uid: uid,
type: 'product'
});
};
module.exports = {
addFavAsync,
cancelFavAsync
};
... ...
/**
* 收藏相关接口
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2016/7/17
*/
'use strict';
const FavAPI = require('./favorite-api');
const toggleFavProduct = (productId, uid, isadd) => {
if (isadd) {
return FavAPI.addFavAsync(uid, productId);
} else {
return FavAPI.cancelFavAsync(uid, productId);
}
};
module.exports = {
toggleFavProduct
};
... ...
... ... @@ -166,7 +166,9 @@ const helpers = {
return b.brandName;
}).join('、');
filters.push(this.newFilter('brand', q.brand, brandNames));
if (brandNames) {
filters.push(this.newFilter('brand', q.brand, brandNames));
}
}
if (q.color) {
... ...
... ... @@ -7,10 +7,10 @@
const api = global.yoho.API;
const getProductBaseAsync = (pid, uid, skn) => {
const getProductBaseAsync = (productId, uid, skn) => {
let params = {
method: 'h5.product.data',
productId: pid
productId: productId
};
if (uid) {
... ... @@ -24,6 +24,15 @@ const getProductBaseAsync = (pid, uid, skn) => {
return api.get('', params, { cache: true });
};
const getUserIsFav = (uid, productId) => {
return api.get('', {
method: 'app.favorite.isFavorite',
id: productId,
uid: uid,
type: 'product'
}, { cache: true });
};
const getsizeInfoAsync = (skn) => {
return api.get('', {
method: 'h5.product.intro',
... ... @@ -39,7 +48,6 @@ const getComfortAsync = productId => {
cache: true,
code: 200
});
};
const getModelTryAsync = skn => {
... ... @@ -54,6 +62,7 @@ const getModelTryAsync = skn => {
module.exports = {
getProductBaseAsync,
getUserIsFav,
getsizeInfoAsync,
getComfortAsync,
getModelTryAsync
... ...
... ... @@ -44,6 +44,98 @@ const _sizeInfoBoSort = sizeInfoBo => {
return sizeInfoBo;
};
/**
* 处理限购商品的有关按钮状态(或取现购买以及底部商品购买按钮)
* @param int uid
* @param int showStatus 限购商品的关联状态
* @param boolean isBeginSale 限购商品是否已开售
*/
const _FashionTopGoodsStatus = (uid, showStatus, isBeginSale) => {
// 潮流尖货状态
let resData = {
getLimitedCode: true // 限购码状态
// hasLimitedCode: false, //是否已经获取限购码
// limitedCodeSoldOut: false, //限购码是否已经抢光
// openSoon: false,//即将开售
// dis: false, //失效
// buyNow: false, //是否立即购买
// soldOut: false, //是否已经售罄
// getLimitedCodeDis: false // 限购码是否失效
};
switch (showStatus) {
case 1: // 开售前/后,立即分享获得限购码(用户未领取限购码)
if (isBeginSale) { // 开售后
Object.assign(resData, {
buyNow: true,
dis: true
});
} else {
Object.assign(resData, {
openSoon: true,
hasLimitedCode: false
});
}
break;
case 2: // 开售后,限购码已抢光(用户未领取限购码)
Object.assign(resData, {
buyNow: true,
dis: true,
limitedCodeSoldOut: true,
getLimitedCode: false,
hasLimitedCode: false
});
break;
case 3: // 开售后,商品已经售罄
Object.assign(resData, {
soldOut: true,
getLimitedCode: false
});
break;
case 4: // 开售后,立即购买(用户已领取限购码)
Object.assign(resData, {
buyNow: true,
dis: false,
hasLimitedCode: true
});
if (!uid) { // 限购码失效
resData.getLimitedCodeDis = true;
}
break;
case 5: // 开售前,限购码已被抢光(用户未领取限购码)
Object.assign(resData, {
openSoon: true,
hasLimitedCode: true,
limitedCodeSoldOut: true,
getLimitedCode: false
});
break;
case 6: // 开售前,即将开售(用户已领取限购码)
Object.assign(resData, {
openSoon: true,
hasLimitedCode: true
});
if (!uid) { // 限购码失效
resData.getLimitedCodeDis = true;
}
break;
case 7: // 开售后,用户已经用获得的限购码购买过商品
Object.assign(resData, {
buyNow: true,
dis: true,
hasLimitedCode: true
});
if (!uid) { // 限购码失效
resData.getLimitedCodeDis = true;
}
break;
default:
break;
}
return resData;
};
const setPathNav = (data, name) => {
let resData = {};
... ... @@ -114,7 +206,8 @@ const setBrandBanner = brand => {
* @return Object
*/
const setProductData = base => {
let data = {
let resData = {
id: base.id,
name: base.productName,
brandName: _.has(base, 'brand.brandName') ? base.brand.brandName : '',
intro: base.salesPhrase,
... ... @@ -125,9 +218,9 @@ const setProductData = base => {
let chooseSkuFlag;
if (_.toNumber(data.sellPrice) >= _.toNumber(data.marketPrice)) {
if (_.toNumber(resData.sellPrice) >= _.toNumber(resData.marketPrice)) {
// 售价与吊牌价相同,只显示售价
_.unset(data, 'marketPrice');
_.unset(resData, 'marketPrice');
}
// 遍历颜色尺寸
... ... @@ -148,7 +241,7 @@ const setProductData = base => {
if (value.goodsImagesList) {
group = {
name: value.colorName,
title: `${data.name} ${value.colorName}`,
title: `${resData.name} ${value.colorName}`,
color: value.colorName,
total: 0
};
... ... @@ -168,8 +261,8 @@ const setProductData = base => {
}
// 默认第一张图片
if (!_.has(data, 'img')) {
data.img = value.colorImage;
if (!_.has(resData, 'img')) {
resData.img = value.colorImage;
}
// 商品尺码列表
... ... @@ -197,13 +290,13 @@ const setProductData = base => {
// 默认选中该sku商品
if (group.total > 0 && !chooseSkuFlag) {
data.colorName = group.name;
resData.colorName = group.name;
group.cur = true; // 选中sku商品
chooseSkuFlag = true;
}
// 商品的总数累加
data.total += size.num;
resData.total += size.num;
sizes.push(size);
});
... ... @@ -213,14 +306,61 @@ const setProductData = base => {
goods.push(group);
});
// 默认设置第一个选中
if (goods.length && !chooseSkuFlag) {
goods[0].focus = true;
goods[0].cur = true;
}
resData.colors = goods;
}
// 限购商品
if (base.isLimitBuy === 'Y') {
let isBeginSale = (base.saleStatus && +base.saleStatus) === 1; // 是否开售
let showStatus = 1; // 限购商品有关的展示状态
if (base.showStatus) {
showStatus = _.toInteger(base.showStatus);
}
let fashTopGoods = _FashionTopGoodsStatus(base.uid, showStatus, isBeginSale);
// 潮流尖货状态
resData.fashionTopGoods = {
getLimitedCode: fashTopGoods.getLimitedCode, // 限购码状态
hasLimitedCode: fashTopGoods.hasLimitedCode, // 是否已经获取限购码
limitedCodeSoldOut: fashTopGoods.limitedCodeSoldOut, // 限购码是否已经抢光
getLimitedCodeDis: fashTopGoods.getLimitedCodeDis // 限购码是否失效
};
if (fashTopGoods.soldOut) {
resData.soldOut = fashTopGoods.soldOut;
// totalStorageNum = 0;//改总数为已售磬
} else {
resData.openSoon = fashTopGoods.openSoon;// 即将开售
resData.dis = fashTopGoods.dis;// 是否失效
resData.buyNow = fashTopGoods.buyNow;// 是否立即购买
}
}
data.colors = goods;
let soldOut = +base.status === 0 || resData.total === 0;
if (+base.attribute === 2) { // 非卖品
resData.notForSale = true;
} else if (soldOut) { // 已售磬
resData.soldOut = true;
_.unset(resData, 'fashionTopGoods');
} else if (+base.attribute === 3) { // 虚拟商品
// 虚拟商品老代码功能有问题,暂不处理
} else {
// 立即购买及即将开售不存在显示加入购物袋
if (!resData.buyNow && !resData.openSoon) {
resData.addToBag = true;
}
}
return data;
return resData;
};
/**
... ...
... ... @@ -5,12 +5,13 @@
*/
'use strict';
const _ = require('lodash');
const itemAPI = require('./item-api');
const itemFUN = require('./item-handler');
const search = require('./search-api');
const _getMultiResourceByBaseInfo = (base, uid) => {
const _getMultiResourceByBaseInfo = (base) => {
let productId = base.id;
let skn = base.erpProductId;
... ... @@ -24,10 +25,8 @@ const _getMultiResourceByBaseInfo = (base, uid) => {
search.getSortAsync({sort: base.smallSortId})
];
if (uid) {
console.log(uid);
// promiseData.push(favoriteBrandService.isFavoriteAsync(uid, productId));
if (base.uid) {
promiseData.push(itemAPI.getUserIsFav(base.uid, productId));
}
return Promise.all(promiseData).then(result => {
... ... @@ -35,12 +34,13 @@ const _getMultiResourceByBaseInfo = (base, uid) => {
sizeInfo: result[0],
comfort: result[1].data,
modelTry: result[2].data,
sort: result[3].data
sort: result[3].data,
productFav: (result[4] && result[4].data)
};
});
};
let getProductItemData = (params) => {
let getProductItemData = (params, uid) => {
let pid = params[0];
// let gid = params[1];
... ... @@ -54,6 +54,8 @@ let getProductItemData = (params) => {
return data;
}
result.uid = uid;
// 商品基本信息
data.goodInfo = itemFUN.setProductData(result);
... ... @@ -68,6 +70,9 @@ let getProductItemData = (params) => {
// 面包屑导航
Object.assign(data, itemFUN.setPathNav(mulRes.sort, result.productName));
// 收藏状态
_.set(data, 'goodInfo.productFav', mulRes.productFav);
// DESCRIPTION商品描述
Object.assign(data, itemFUN.setDescriptionData(mulRes.sizeInfo, mulRes.comfort));
... ...
... ... @@ -10,6 +10,7 @@ const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const list = require(cRoot + '/list');
const item = require(cRoot + '/item');
const fav = require(cRoot + '/favorite');
const shop = require(cRoot + '/shop');
const query = require(cRoot + '/query');
... ... @@ -18,6 +19,7 @@ router.get('/list', list.index); // 列表页面
router.get('/list/new', list.newPage); // 新品列表页
router.get(/\/item\/([\d]+)_([\d]+).html/, item.index); // 商品详情页
router.post('/item/togglecollect', fav.product); // 商品详情页
router.get('/shop/:domain/list', shop.list);
router.get('/shop/:domain', shop.index);
... ...
... ... @@ -6,7 +6,7 @@
{{> path-nav}}
{{# goodInfo}}
<div class="product-main clearfix">
<div class="product-main clearfix" data-id="{{id}}">
<div class="thumbs left clearfix">
<div class="thumb-list hide">
{{# colors}}
... ... @@ -67,30 +67,64 @@
{{/ colors}}
</div>
</div>
<p class="btns">
<div class="tip-content">
{{# fashionTopGoods}}
{{#if buyNow}}
<span id="buy-now" class="btn">立即购买</span>
{{#if getLimitedCode}}
<span>
点击
<i class="get-lc{{#unless getLimitedCodeDis}} blue{{/unless}}">获取限购码</i>
,凭借限购码购买商品
</span>
<div class="lc-container hide">
<span class="lc-arrow"></span>
<div class="lc-content">
<div class="qr-code"></div>
<p class="title">打开APP扫描二维码获取限购码</p>
<p class="sub-title">商品开售后即可购买</p>
</div>
</div>
{{/if}}
{{#if openSoon}}
<span class="btn">即将开售</span>
{{#if hadLimitedCode}}
<span>您已获取限购码</span>
{{/if}}
{{#if limitedCodeSoldOut}}
<span>限购码已抢光</span>
{{/if}}
{{^}}
<span id="add-to-bag" class="btn">
<i class="iconfont">&#xe632;</i>
加入购物袋
</span>
{{/ fashionTopGoods}}
</div>
<p class="btns">
{{#unless soldOut}}
{{# buyNow}}
<span id="buy-now" class="btn{{#if dis}} disable{{/if}}" data-base="{{buyNowBase}}">立即购买</span>
{{/ buyNow}}
{{# openSoon}}
<span id="open-soon" class="btn disable">即将开售</span>
{{/ openSoon}}
{{#if notForSale}}
<span class="btn disable">
<i class="iconfont">&#xe62d;</i>
非卖品
</span>
{{/if}}
{{#if addToBag}}
<span id="add-to-bag" class="btn">
<i class="iconfont">&#xe632;</i>
加入购物袋
</span>
{{/if}}
{{/unless}}
<span id="sold-out" class="btn disable hide">已售罄</span>
<span id="collect-product" class="btn white">
<span id="collect-product" class="btn white{{#if productFav}} coll{{/if}}">
<span class="collecting">
<i class="iconfont">&#xe644;</i>
收藏商品
</span>
<span class="collected">
<i class="iconfont">&#xe627;</i>
已收藏
<em>已收藏</em>
<em class="cal-col">取消收藏</em>
</span>
</span>
</p>
... ...
... ... @@ -13,7 +13,7 @@
<span>{{total}}</span>件商品
</label>
<label class="page-info"><span class="cur-page">{{page}}</span>/<span class="total-page">{{pageTotal}}</span></label>
{{#isEqual page 1}}
{{#isEqual page '1'}}
<span class="iconfont page disable page-pre">&#xe607;</span>
{{^}}
<span class="iconfont page page-pre">&#xe607;</span>
... ...
... ... @@ -66,3 +66,11 @@ $clearInput.click(function() {
$searchKey.val('');
$(this).hide();
});
$(document).click(function(e) {
var $tar = $(e.target);
if (!$tar.closest('.search-entry').length) {
$searchWrap.hide();
}
});
... ...
... ... @@ -6,7 +6,8 @@
var $ = require('yoho-jquery'),
lazyload = require('yoho-jquery-lazyload');
var $mainThumb = $('#main-thumb'),
var $main = $('.product-main'),
$mainThumb = $('#main-thumb'),
$checkBtns = $('.check-btns');
var $tradeWrapper = $('.trade-wrapper'),
... ... @@ -25,7 +26,8 @@ var $thumbCur = $('.thumb-wrap .cur'); // 当前选中展示图片
var SLIDETIME = 200;
var thumbsLoaded = [];
var thumbsLoaded = [],
id = $main.data('id');
require('../common/header');
... ... @@ -86,8 +88,9 @@ function chooseColor(index) {
}
// 控制才显示对应颜色尺码
$sizeList.not('.hide').addClass('hide');
$sizeList.not('.hide').addClass('hide').children('.cur').removeClass('cur');
$sizeList.eq(index).removeClass('hide');
$sizeText.text('');
}
// 缩略图垂直居中
... ... @@ -129,7 +132,7 @@ $('.color-list').on('click', '.color-item', function() {
index = $this.index(),
data = $this.data();
if (!$this.hasClass('cur')) {
if (!$this.children().hasClass('cur')) {
$colorItem.removeClass('cur');
$colorText.text(data.color ? data.color : '');
$this.children().addClass('cur');
... ... @@ -178,3 +181,28 @@ $addToBag.click(function() {
});
}
});
$('#collect-product').click(function() {
var $this = $(this),
param = {
productId: id,
type: 'add'
};
if ($this.hasClass('coll')) {
param.type = 'cancel';
}
$.ajax({
type: 'POST',
url: '/product/item/togglecollect',
data: param
}).then(function(data) {
if (data.code === 200) {
$this.toggleClass('coll');
} else if (data.code === 403) {
location.href = data.data.refer;
}
});
});
... ...
... ... @@ -176,11 +176,11 @@ var YohoListPage = {
if (!$(this).hasClass('disable')) {
if ($(this).hasClass('page-pre')) {
YohoListPage.go({
page: YohoListPage.page - 1
page: parseInt(YohoListPage.page, 10) - 1
});
} else {
YohoListPage.go({
page: YohoListPage.page + 1
page: parseInt(YohoListPage.page, 10) + 1
});
}
}
... ...
... ... @@ -84,7 +84,7 @@
.option-content {
width: 226px;
margin: 0 auto;
padding: 40px 0 30px;
padding: 40px 0 20px;
> p {
line-height: 50px;
... ... @@ -133,6 +133,58 @@
}
}
.tip-content {
text-align: center;
font-size: 14px;
line-height: 1.5;
padding-bottom: 10px;
font-size: 14px;
position: relative;
.blue {
margin: 0 6px;
cursor: pointer;
}
}
.lc-container {
width: 300px;
height: 120px;
margin-top: 4px;
margin-left: -150px;
padding: 20px 20px 20px 120px;
position: absolute;
left: 50%;
background: #fff;
border: 2px solid #bcbcbc;
font-size: 12px;
text-align: left;
color: #888;
.lc-arrow {
width: 12px;
height: 7px;
background: url('/product/code-horn.png') no-repeat;
position: absolute;
top: -7px;
left: 85px;
}
.qr-code {
width: 90px;
height: 90px;
position: absolute;
top: 12px;
left: 14px;
}
.title {
font-size: 16px;
color: #1d1d1d;
margin-bottom: 14px;
}
}
.btn {
width: 300px;
height: 46px;
... ... @@ -148,9 +200,22 @@
font-size: 14px;
}
.collected {
}
.collected {
display: none;
.cal-col {
display: none;
}
&:hover em {
display: none;
}
&:hover .cal-col {
display: inline;
}
}
.coll {
... ...