Authored by huangyCode

Merge branch 'feature/group-buy' of http://git.yoho.cn/fe/yohobuywap-node into feature/group-buy

... ... @@ -28,7 +28,6 @@ function index(req, res, next) {
function groupListIndex(req, res, next) {
req.ctx(GroupService).groupList()
.then(result => {
return res.render('group/group-list', {
... ... @@ -61,16 +60,19 @@ function groupList(req, res, next) {
}
function progress(req, res, next) {
req.ctx(GroupService).index()
.then(result => {
const uid = req.user.uid;
const groupNo = req.query.groupNo;
const activityId = req.query.activityId;
req.ctx(GroupService).groupResult({groupNo, activityId, uid})
.then(renderData => {
return res.render('group/progress', {
title: '拼团详情',
page: 'group-progress',
localCss: true,
nodownload: true,
width750: true,
wechatShare: true,
floors: result
data: renderData
});
}).catch(next);
}
... ... @@ -110,11 +112,12 @@ function goodsDetail(req, res, next) {
}
function order(req, res, next) {
const uid = req.user.uid.toString();
const uid = req.user.uid;
const { type, selectIndex } = req.query;
const param = {
page: 1,
limit: 50,
type: 2,
type: type || 1,
uid
};
... ... @@ -127,7 +130,18 @@ function order(req, res, next) {
nodownload: true,
width750: true,
wechatShare: true,
result
result,
navs: [
{
title: '进行中',
src: '/activity/group/order?type=2&selectIndex=0'
},
{
title: '全部',
src: '/activity/group/order?type=1&selectIndex=1'
}
],
selectIndex: Number(selectIndex) || 0
});
}).catch(next);
}
... ...
... ... @@ -81,15 +81,17 @@ class GroupApi extends global.yoho.BaseModel {
/**
* 拼团详情
* @param uid
* @param groupNo
* @returns {Promise|Promise<T>}
*/
groupDetail({groupNo} = {}) {
groupDetail({groupNo, uid} = {}) {
return this.get({
url: '',
data: {
method: 'app.activity.groupDetail',
groupNo: groupNo
uid,
groupNo,
method: 'app.activity.groupDetail'
},
param: {code: 200}
}).then((result) => {
... ... @@ -102,14 +104,20 @@ class GroupApi extends global.yoho.BaseModel {
/**
* 活动拼团商品推荐
* @param activityId
* @param uid
* @param limit
* @param page
* @returns {Promise|Promise<T>}
*/
activityRecomend({activityId} = {}) {
activityRecommend({activityId, uid, limit = 20, page = 1} = {}) {
return this.get({
url: '',
data: {
uid,
page,
limit,
activityId,
method: 'app.collage.productList.detail',
activityId: activityId
},
param: {code: 200}
}).then((result) => {
... ...
... ... @@ -19,6 +19,72 @@ class GroupService extends global.yoho.BaseModel {
}
}
async groupResult({groupNo, activityId, uid} = {}) {
try {
const [detail, recommend] = await Promise.all([
this.api.groupDetail({
uid,
groupNo
}),
this.api.activityRecommend({
uid,
activityId
})
]);
// pageGo状态值:
// 1. 开团成功--准备邀请好友参团
// 2. 尚未加入团--应好友邀请
// 3. 已经加入团--应好友邀请
// 4. 团已达成--你已加入一起购买成功
// 5. 团已达成--你来晚了没能加入
// 6. 拼团失败--过期未达成
// 7. 拼团失败--过期未达成
let yourJoinItem = detail.yourJoinItem;
let lackNum = detail.lackNum;
let membershipItems = detail.membershipItems;
if (!yourJoinItem) {
detail.yourJoinItem = membershipItems[0];
}
for (let i = 0; i < +lackNum; i++) {
detail.membershipItems.push({
empty: true
});
}
return {
...detail,
recommend
};
} catch (e) {
throw new Error('Group result fail to load data.');
}
}
async groupResultRec({activityId, uid, page, limit} = {}) {
try {
const recommend = await this.api.activityRecommend({
uid,
activityId,
limit,
page
});
return {
recommend
};
} catch (e) {
throw new Error('Group index fail to load resources.');
}
}
async tabData() {
const result = await this.api._getPromoteCount();
let tabsData = {};
... ... @@ -102,13 +168,15 @@ class GroupService extends global.yoho.BaseModel {
async order({
type,
page,
limit
limit,
uid
}) {
try {
const result = await this.api.getOrderList({
limit,
page,
type
type,
uid
});
console.log(result);
... ...
... ... @@ -13,5 +13,5 @@
</div>
</div>
<a class="my-group my-group-handler" href="/activity/group/progress">我的拼团</a>
<a class="my-group my-group-handler" href="/activity/group/order">我的拼团</a>
<div class='my-share'></div>
\ No newline at end of file
... ...
<div class="group">
<div class="resources">
{{#each floors}}
{{#ifcond template_name "==" 'focus'}}
{{> group/resources/focus}}
{{/ifcond}}
{{#ifcond template_name "==" 'newSingleImage'}}
{{> group/resources/new-single-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'twoPicture'}}
{{> group/resources/two-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'image_list'}}
{{> group/resources/four-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'splitJointImg'}}
{{> group/resources/split-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'collageBuyPrdList'}}
{{> group/resources/collage-buy-prd-list}}
{{/ifcond}}
{{/each}}
<div class="floors">
{{#each floors}}
{{#ifcond template_name "==" 'focus'}}
{{> group/resources/focus}}
{{/ifcond}}
{{#ifcond template_name "==" 'newSingleImage'}}
{{> group/resources/new-single-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'twoPicture'}}
{{> group/resources/two-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'image_list'}}
{{> group/resources/four-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'splitJointImg'}}
{{> group/resources/split-image}}
{{/ifcond}}
{{#ifcond template_name "==" 'collageBuyPrdList'}}
{{> group/resources/collage-buy-prd-list}}
{{/ifcond}}
{{/each}}
</div>
<div id='fixedTab' class="tab-filter">
{{!-- {{#if floatTab}} float{{/if}}{{#if !tabs.showTab}} only-filter{{/if}} --}}
{{#if tabs.showTab}}
<div class="tab">
<div class="tab group-tab">
<div class="tab-item">
<div class="tiptext active" data-channel="newGroup">邀新团</div>
{{!-- {{#if isNewGroup}} --}}
<div class="tab-line"></div>
{{!-- {{/if}} --}}
</div>
<div class="tab-item">
<div class="tiptext" data-channel="normalGroup">普通团</div>
{{!-- {{#if !isNewGroup}} --}}
{{!-- <div class="tab-line"></div> --}}
{{!-- {{/if}} --}}
</div>
</div>
{{/if}}
... ... @@ -51,6 +46,8 @@
</div>
{{/if}}
{{> common/filter}}
</div>
<a class="bottom" href="/activity/group/progress">我的拼团</a>
<a class="bottom" href="/activity/group/order">我的拼团</a>
</div>
\ No newline at end of file
... ...
<div class="group-order">
我的拼团
{{#if navs.length}}
<div class="group-order-header">
{{#each navs}}
{{#ifcond @index '===' ../selectIndex }}
<a href={{this.src}} class="group-normal active">{{this.title}}</a>
{{^}}
<a href={{this.src}} class="group-normal">{{this.title}}</a>
{{/ifcond}}
{{/each}}
</div>
{{/if}}
{{#if result.order_list}}
{{else}}
<div class="group-no-order-list">暂无数据</div>
{{/if}}
</div>
... ...
<div class="group-progress">
{{#data}}
<div class="card">
<div class="inner-card">
<img
src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="card-pre-img">
<img src="{{image2 yourJoinItem.productIcon w=200 h=282}}" alt="" class="card-pre-img">
<div class="info">
<div class="name">{{yourJoinItem.productName}}</div>
<div class="group-price">{{yourJoinItem.productGroupPrice}}</div>
<div class="single-buy-price">单人购买:<span class="line-through">{{yourJoinItem
.productSalePrice}}</span></div>
</div>
</div>
<div class="members">
{{#membershipItems}}
{{#if empty}}
<div class="meb-item empty"></div>
{{else}}
<div class="meb-item">
<img class="avatar" src="{{image2 headUrl w=160 h=160}}" alt="">
{{#if @first}}
<span class="leader-badge"></span>
{{/if}}
</div>
{{/if}}
{{/membershipItems}}
</div>
{{#ifcond pageGo '===' 7}}
<div class="status-text">拼团失败</div>
<div class="status-btn">查看更多拼团活动</div>
{{/ifcond}}
{{#ifcond pageGo '===' 6}}
<div class="status-text">拼团失败</div>
{{/ifcond}}
{{#ifcond pageGo '===' 4}}
<div class="status-text">拼团成功</div>
{{/ifcond}}
{{#ifcond pageGo '===' 5}}
<div class="status-text">你来晚了 你的好友拼团已经成功</div>
{{/ifcond}}
<a class="tip" href="http://m.yohobuy.com/activity/group">支付开团-支付参团-凑齐人数发货-凑不齐退款 玩法介绍》</a>
</div>
{{/data}}
<div class="divide"></div>
<div class="banner"></div>
<div class="divide zero-margin"></div>
<div class="recommend">
<p class="rec-title">看看其他拼团商品</p>
{{# @root.data.recommend.collageProductVoList}}
<div class="product-item">
<img src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="prd-item-img">
</div>
<div class="product-item">
<img src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="prd-item-img">
</div>
<div class="product-item">
<img src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="prd-item-img">
</div>
<div class="product-item">
<img src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="prd-item-img">
</div>
<div class="product-item">
<img src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="prd-item-img">
</div>
<div class="product-item">
<img src="http://img13.static.yhbimg.com/goodsimg/2015/08/13/03/0294746e1d4e614c28dc8736dfca66d173.jpg?imageMogr2/thumbnail/200x284/background/d2hpdGU=/position/center/quality/80" alt="" class="prd-item-img">
<img src="{{image2 defaultImages w=200 h=282}}" alt="" class="prd-item-img">
<div class="prd-info">
<div class="prd-name">{{productName}}</div>
<div class="price">
<span class="cprice">¥{{collagePrice}}</span>
<span class="mprice">¥{{marketPrice}}</span>
</div>
{{#ifcond joinPeopleNum '>' 0}}
<span class="joined">{{joinPeopleNum}} 人已參加</span>
{{/ifcond}}
<div class="buy-btn">
<span class="num">{{peopleNum}}人成团</span>
<span class="immediate">立即购买</span>
</div>
</div>
</div>
{{/@root.data.recommend.collageProductVoList}}
</div>
</div>
... ...
... ... @@ -5,17 +5,17 @@
<span class="iconfont drop">&#xe613;</span>
</a>
</li> --}}
<li class="new active" data-order="s_t_desc">
<li class="new active" data-type="new" data-order="s_t_desc">
<a href="javascript:void(0);">
<span class="span-test">新品</span>
</a>
</li>
<li class="popularity" data-order="h_v_desc">
<li class="popularity" data-type="popularity" data-order="h_v_desc">
<a href="javascript:void(0);">
<span class="span-test">人气</span>
</a>
</li>
<li class="price">
<li class="price" data-type="price">
<a href="javascript:void(0);">
<span class="span-test">价格</span>
<span class="icon">
... ...
... ... @@ -3,16 +3,131 @@ import Swiper from 'yoho-swiper';
import $ from 'yoho-jquery';
import Page from 'js/yoho-page';
// import lazyLoad from 'yoho-jquery-lazyload';
// import tip from 'js/plugin/tip';
// import filter from 'js/plugin/filter';
// import loading from 'js/plugin/loading';
class Group extends Page {
constructor() {
super();
this.selector = {};
this.selector = {
tabSection: $('#fixedTab'),
floorsContentHeight: $('.floors').height(),
groupTab: $('.group-tab'),
filterTab: $('.filter-nav'),
groupListContent: $('.group-list')
};
this.navInfo = {
new: {
order: 1,
reload: true,
page: 1,
end: false
},
popularity: {
order: 1,
reload: true,
page: 1,
end: false
},
price: {
order: 1,
reload: true,
page: 0,
end: false
}
};
this.noResult = '<p class="no-result">未找到相关搜索结果</p>';
this.$pre = this.selector.filterTab.find('.active');
this.selectedChannel = 'newGroup';
this.filterTab = {
newGroup: {
joinLimit: 1,
order: 's_t_desc'
},
normalGroup: {
joinLimit: 2,
order: 's_t_desc'
}
};
this.filterPage = {
newGroup: {},
normalGroup: {}
};
this.bindEvents();
this.swiperTop();
$(window).scroll(() => {
window.requestAnimationFrame(this.scrollHandler.bind(this));
});
}
scrollHandler() {
// 滚动固定tab
let floorsContentHeight = this.selector.floorsContentHeight + 5;
if ($(window).scrollTop() > floorsContentHeight) {
this.selector.tabSection.addClass('float');
this.selector.groupListContent.addClass('mrt');
} else {
this.selector.tabSection.removeClass('float');
this.selector.groupListContent.removeClass('mrt');
}
}
bindEvents() {
this.selector.tabSection.on('click', this.fixedTab.bind(this));
this.selector.groupTab.on('click', this.groupTabChange.bind(this));
this.selector.filterTab.on('click', 'li', this.filterTabChange.bind(this));
}
fixedTab() {
let listHeight = this.selector.groupListContent.height();
let windowHeight = $(window).height();
// 商品列表超过一屏时
if (listHeight > windowHeight || listHeight === windowHeight) {
if (!this.selector.tabSection.hasClass('float')) {
this.selector.tabSection.addClass('float');
$(window).scrollTop(this.selector.floorsContentHeight + 10);
}
}
}
filterTabChange(e) {
let $this = $(e.currentTarget);
let order = $this.data('order');
let navType = $this.data('type');
let currentChannel = this.selectedChannel;
let nav = this.navInfo[navType];
if ($this.hasClass('new') || $this.hasClass('popularity')) {
this.selector.filterTab.children('li').removeClass('active');
$this.addClass('active');
this.filterTab[currentChannel].order = order;
this.search();
}
if ($this.hasClass('price') || $this.hasClass('discount')) {
// 价格/折扣切换排序状态
$this.find('.icon > .iconfont').toggleClass('cur');
nav.reload = true; // 重置reload,HTML会被替换为逆序的HTML
nav.order = nav.order === 0 ? 1 : 0; // 切换排序
}
}
bindEvents() {}
groupTabChange(e) {
let target = $(e.target);
let channel = target.data('channel');
if (!target.hasClass('active')) {
this.selector.groupTab.find('.tiptext').removeClass('active');
target.addClass('active');
}
this.selectedChannel = channel;
this.search();
}
// 顶部swiper
swiperTop() {
... ... @@ -29,6 +144,67 @@ class Group extends Page {
});
}
}
//
/**
* 筛选注册的回调,筛选子项点击后逻辑
* 需要执行search的场景:1.点选筛选项;2.下拉加载
* @param opt
*/
search() {
let params = this.filterTab[this.selectedChannel];
console.log(params);
// let setting;
// loading.showLoadingMask();
// $.ajax({
// type: 'GET',
// url: '/product/sale/search',
// data: setting,
// success: function() {
// },
// error: function() {
// tip.show('网络断开连接了~');
// loading.hideLoadingMask();
// }
// });
}
// 筛选初始化
// filterInit(option) {
// let $filterMask;
// $.ajax({
// type: 'GET',
// url: '/product/sale/filter',
// data: $.extend(defaultOpt, {
// saleType: '1'
// }, option),
// success: function(data) {
// $filterMask && $filterMask.remove();
// this.selector.groupListContent.append(data);
// $filterMask = $('.filter-mask');
// // 初始化filter&注册filter回调
// filter.initFilter({
// fCbFn: this.search,
// hCbFn: function() {
// // 切换active状态到$pre上
// this.$pre.addClass('active');
// this.$pre.siblings().removeClass('active');
// },
// missStatus: true
// });
// }
// });
// }
}
$(() => {
... ...
.group-order {
height: 100px;
.group-order-header {
border-bottom: 2px solid #e0e0e0;
background: #fff;
position: fixed;
z-index: 1001;
display: flex;
top: 0;
flex-direction: row;
justify-content: space-around;
align-items: center;
height: 81px;
width: 100%;
.group-normal {
color: #b0b0b0;
font-size: 28px;
}
.active {
color: #444;
}
}
.group-no-order-list {
margin-top: 81px;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
}
... ...
... ... @@ -7,7 +7,34 @@
height: 690px;
margin: 0 auto;
background: #fff;
box-shadow: 0 10px 32px -4px rgba(205, 205, 205, 0.5);
box-shadow: 0 0 20px rgba(205, 205, 205, 0.5);
}
.status-text {
position: absolute;
bottom: 250px;
left: 50%;
transform: translateX(-50%);
font-size: 36px;
color: #444;
letter-spacing: -1px;
text-align: center;
font-weight: bold;
}
.status-btn {
position: absolute;
bottom: 130px;
left: 50%;
height: 80px;
padding: 0 50px;
font-size: 32px;
color: #fff;
text-align: center;
line-height: 80px;
border-radius: 40px;
background-color: #000;
transform: translateX(-50%);
}
.inner-card {
... ... @@ -17,7 +44,7 @@
width: 600px;
height: 282px;
background: #fff;
box-shadow: 0 10px 20px -4px rgba(0, 0, 0, 0.17);
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.inner-card .card-pre-img {
... ... @@ -28,6 +55,68 @@
height: 282px;
}
.inner-card .info {
height: 282px;
padding: 34px 20px 0 230px;
box-sizing: border-box;
.group-price {
font-size: 40px;
color: #d0021b;
font-weight: bold;
}
.single-buy-price {
font-size: 20px;
color: #b0b0b0;
}
.single-buy-price .line-through {
text-decoration: line-through;
}
}
.members {
position: absolute;
top: 194px;
left: 0;
right: 0;
font-size: 0;
text-align: center;
}
.meb-item {
position: relative;
display: inline-block;
width: 80px;
height: 80px;
margin-left: 40px;
border-radius: 40px;
&:first-child {
margin-left: 0;
}
&.empty {
background-image: url("img/activity/group/member-empty.png");
background-size: 80px 80px;
}
.avatar {
border-radius: 40px;
}
.leader-badge {
position: absolute;
top: 0;
left: 0;
width: 80px;
height: 80px;
background-image: url("img/activity/group/member-leader.png");
background-size: 80px 80px;
}
}
.tip {
position: absolute;
right: 0;
... ... @@ -77,5 +166,79 @@
height: 260px;
width: 200px;
}
.prd-info {
position: relative;
padding-left: 220px;
height: 260px;
.prd-name {
font-size: 28px;
color: #444;
line-height: 1.4;
}
}
.prd-info .price {
font-size: 0;
margin-top: 30px;
}
.prd-info .price .cprice {
font-size: 36px;
color: #d0021b;
font-weight: bold;
}
.prd-info .price .mprice {
font-size: 24px;
color: #b0b0b0;
margin-left: 20px;
text-decoration: line-through;
}
.prd-info .joined {
position: absolute;
bottom: 0;
left: 240px;
font-size: 24px;
color: #444;
font-weight: 400;
}
.prd-info .buy-btn {
position: absolute;
bottom: 0;
right: 0;
width: 240px;
height: 60px;
font-size: 0;
border-radius: 30px;
border: 1px solid #d0021b;
}
.buy-btn .num {
display: inline-block;
width: 50%;
height: 100%;
font-size: 24px;
color: #d0021b;
line-height: 58px;
text-align: center;
}
.buy-btn .immediate {
display: inline-block;
width: 50%;
height: 100%;
font-size: 24px;
color: #fff;
line-height: 58px;
text-align: center;
letter-spacing: -0.34px;
background-color: #d0021b;
border-top-right-radius: 30px;
border-bottom-right-radius: 30px;
}
}
}
... ...
... ... @@ -27,16 +27,21 @@
border-top: 1px solid #e0e0e0;
}
.mrt {
margin-top: 230px;
}
.floors {
border-bottom: 10px solid #f0f0f0;
}
.tab {
width: 100%;
height: wrap;
display: flex;
flex-direction: row;
align-items: center;
background-color: white;
z-index: 1000;
border-bottom: 1px solid #e0e0e0;
border-top: 10px solid #f0f0f0;
.tab-item {
position: relative;
... ... @@ -53,42 +58,25 @@
font-size: 32px;
color: #b0b0b0;
flex: 1;
width: wrap;
text-align: center;
height: 100%;
display: flex;
flex-wrap: wrap;
align-content: center;
flex-direction: row;
align-items: center;
width: 60%;
line-height: 80px;
border-bottom: solid 4px transparent;
font-weight: 500;
}
.tiptext.active {
color: #444;
}
.tab-line {
position: absolute;
bottom: -2px;
left: 50%;
background-color: black;
width: 180px;
height: 4px;
transform: translateX(-50%);
border-bottom: solid 4px #000;
}
.tab-filter.float {
position: fixed;
top: -22px;
top: 0;
left: 0;
right: 0;
border-top: none;
z-index: 998;
background-color: #fff;
}
.tab-filter.float.only-filter {
top: -1px;
}
}
... ...