Authored by 孙凯

add 团购 UI review by daiqiang

'use strict';
import React from 'react';
import Immutable, {Map} from 'immutable';
import YH_Image from '../../common/components/YH_Image';
import {getSlicedUrl} from '../../classify/utils/Utils';
import GroupDetailProduct from './GroupDetailProduct';
import HeaderList from './HeaderList';
import TimerMixin from 'react-timer-mixin';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
export default class GroupDetailHeader extends React.Component {
constructor(props) {
super(props);
this._renderTip = this._renderTip.bind(this);
this._renderTime = this._renderTime.bind(this);
this.startTimer = this.startTimer.bind(this);
this.stopTimer = this.stopTimer.bind(this);
this.formatDate = this.formatDate.bind(this);
this.format = this.format.bind(this);
this.state = {
formatTime : null,
};
}
componentDidMount() {
this.startTimer();
}
componentWillUnmount() {
this.stopTimer();
}
startTimer() {
this.stopTimer();
this.timer = TimerMixin.setInterval(() => {
diffTime = diffTime-1;
this.props.updateTime && this.props.updateTime(diffTime)
if (diffTime < 0) {
this.stopTimer();
}
if (diffTime > 0) {
let formatTime = this.formatDate(diffTime);
this.setState({
formatTime,
});
}
}, 1000);
}
stopTimer() {
this.timer && TimerMixin.clearTimeout(this.timer);
}
_renderTip(tipTitle,tipDetail) {
return(
<View style={styles.container}>
<Text style={styles.tipTitle}>{tipTitle}</Text>
<Text style={styles.tipDetail}>{tipDetail}</Text>
</View>
);
}
_renderTime(lackNum,formatTime) {
if(!formatTime || diffTime <= 0)return null;
return(
<View style={styles.container}>
<View style={styles.productdetailTimeCountView}>
<Text style={styles.productdetailTimeCount}>还差</Text>
<Text style={styles.productdetailTimeRedCount}>{lackNum}</Text>
<Text style={styles.productdetailTimeCount}>人加入,剩余时间</Text>
</View>
<View style={styles.productdetailTimeCountView1}>
<Text style={styles.timeItem}>{formatTime.h1 ? formatTime.h1 : 0}</Text>
<Text style={styles.timeItem}>{formatTime.h2 ? formatTime.h2 : 0}</Text>
<Text style={styles.timeSp}>:</Text>
<Text style={styles.timeItem}>{formatTime.m1 ? formatTime.m1 : 0}</Text>
<Text style={styles.timeItem}>{formatTime.m2 ? formatTime.m2 : 0}</Text>
<Text style={styles.timeSp}>:</Text>
<Text style={styles.timeItem}>{formatTime.s1 ? formatTime.s1 : 0}</Text>
<Text style={styles.timeItem}>{formatTime.s2 ? formatTime.s2 : 0}</Text>
</View>
</View>
);
}
render() {
let { groupDetail } = this.props;
let resource = groupDetail ? groupDetail.toJS():null;
if(!resource){
return null;
}
let groupId = resource.groupId;
let leftTime = resource.leftTime;
let isNewCustomer = resource.isNewCustomer;
let pageGo = resource.pageGo;
let groupRole = resource.groupRole;
let lackNum = resource.lackNum;
let openerJoinItem = resource.openerJoinItem;
let yourJoinItem = resource.yourJoinItem;
let membershipItems = resource.membershipItems;
let productDetail = membershipItems ? membershipItems[0]: '';
let joinLimit = resource.joinLimit;
let perpleNum = membershipItems.length + lackNum;
let buttonText = '';
let tipTitle = '';
let tipDetail = '';
if(diffTime == 0 ){
diffTime = parseInt(leftTime);
}
if(pageGo == 1 || pageGo == 2 || pageGo == 3){
this.startTimer();
}else {
this.stopTimer();
}
if(pageGo == 1) {
let subtext = joinLimit==1 ? '(仅限新人)' : '';
buttonText = '邀请小伙伴拼团' + subtext;
}else if (pageGo == 2) {
buttonText = '去参团';
}else if (pageGo == 3) {
let subtext = joinLimit==1 ? '(仅限新人)' : '';
buttonText = '邀请小伙伴拼团' + subtext;
}else if (pageGo == 4) {
tipTitle = '拼团成功';
tipDetail = '我们会尽快安排发货';
buttonText = '查看更多拼团活动';
}else if (pageGo == 5) {
tipTitle = '你来晚了';
tipDetail = '你的好友已经拼团成功';
buttonText = '查看更多拼团活动';
}else if (pageGo == 6) {
tipTitle = '拼团失败';
tipDetail = '购买人数不足,我们会尽快安排退款';
buttonText = '查看更多拼团活动';
}else if (pageGo == 7) {
tipTitle = '拼团失败';
tipDetail = '购买人数不足,我们会尽快安排退款';
buttonText = '查看更多拼团活动';
}else {
tipTitle = '拼团失败';
tipDetail = '购买人数不足,我们会尽快安排退款';
buttonText = '查看更多拼团活动';
}
return (
<View style={styles.container}>
<View style={styles.detail}>
<View style={styles.headerList}>
<HeaderList resource={membershipItems} lackNum={lackNum}/>
</View>
{pageGo == 1 || pageGo == 2 || pageGo == 3
?
this._renderTime(lackNum,this.state.formatTime)
:
this._renderTip(tipTitle,tipDetail)
}
<TouchableOpacity activeOpacity={0.5} style={styles.button} onPress={() => {
this.props.didTouchButton && this.props.didTouchButton(resource);
}}>
<Text style={styles.buttonText}>{buttonText}</Text>
</TouchableOpacity>
<Text style={styles.tip}>支付邀请-支付加入-凑齐人数发货-凑不齐退款 玩法介绍》</Text>
</View>
<View style={styles.product}>
<GroupDetailProduct resource={productDetail} perpleNum={perpleNum}/>
</View>
<View style={styles.bottomLine}/>
</View>
);
}
format(m) {
if (m < 10){
return [0, m];
}else {
var h1 = Math.floor(m / 10);
var h2 = m - h1*10;
return [h1, h2];
}
return [0,0];
}
formatDate(shijianchuo) {
// 秒数
var second = Math.floor(shijianchuo);
// 小时位
var hr = Math.floor(second / 3600);
var hrFormat = this.format(hr);
// 分钟位
var min = Math.floor((second - hr * 3600) / 60);
var minFormat = this.format(min);
// 秒位
var sec = (second - hr * 3600 - min * 60);
var secFormat = this.format(sec);
var tFormat = {
h1: hrFormat[0],
h2: hrFormat[1],
m1: minFormat[0],
m2: minFormat[1],
s1: secFormat[0],
s2: secFormat[1],
};
return tFormat;
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let diffTime = 0;
let styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
product: {
position: 'absolute',
alignItems: 'center',
top: 20*DEVICE_WIDTH_RATIO,
},
detail: {
flex: 1,
width: width - 30*DEVICE_WIDTH_RATIO,
marginTop: 84*DEVICE_WIDTH_RATIO,
shadowColor: 'rgba(0,0,0,0.2)',
shadowOffset: {width: 0, height: 5},
shadowOpacity: 0.5,
shadowRadius: 5,
elevation: 4,
alignItems: 'center',
backgroundColor: 'white',
},
headerList: {
marginTop: 107*DEVICE_WIDTH_RATIO,
width: width - 30*DEVICE_WIDTH_RATIO,
height: 40*DEVICE_WIDTH_RATIO,
},
button: {
height: 40*DEVICE_WIDTH_RATIO,
borderRadius: 20*DEVICE_WIDTH_RATIO,
backgroundColor: '#D0021B',
flex: 1,
justifyContent: 'center',
paddingLeft: 30*DEVICE_WIDTH_RATIO,
paddingRight: 30*DEVICE_WIDTH_RATIO,
marginTop: 30*DEVICE_WIDTH_RATIO,
},
buttonText: {
fontSize: 16,
color: '#FFFFFF',
},
tip: {
fontSize: 12,
color: '#B0B0B0',
marginTop: 18*DEVICE_WIDTH_RATIO,
marginBottom: 20*DEVICE_WIDTH_RATIO,
},
bottomLine: {
width,
height: 8*DEVICE_WIDTH_RATIO,
backgroundColor: '#F0F0F0',
marginTop: 19*DEVICE_WIDTH_RATIO,
},
tipTitle: {
fontSize: 18,
color: '#444444',
marginTop: 23*DEVICE_WIDTH_RATIO,
fontWeight: 'bold',
},
tipDetail: {
fontSize: 12,
color: '#B0B0B0',
marginTop: 8*DEVICE_WIDTH_RATIO,
},
productdetailTimeCountView: {
width,
height: 22,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginTop: 19*DEVICE_WIDTH_RATIO,
},
productdetailTimeCountView1: {
width,
height: 22,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginTop: 10*DEVICE_WIDTH_RATIO,
},
productdetailTimeRedCount: {
fontSize: 12,
color: '#D0021B',
},
productdetailTimeCount: {
fontSize: 12,
color: '#444444',
},
timeItem: {
width: 18,
height: 22,
lineHeight: 22,
fontSize: 14,
color: 'white',
textAlign: 'center',
marginLeft: 4,
backgroundColor: '#444444',
},
timeSp :{
fontSize: 14,
color: '#444444',
textAlign: 'center',
marginLeft: 4,
},
});
... ...
'use strict';
import React from 'react';
import Immutable, {Map} from 'immutable';
import YH_Image from '../../common/components/YH_Image';
import {getSlicedUrl} from '../../classify/utils/Utils';
import DeleteLineText from '../../common/components/DeleteLineText';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
export default class GroupDetailProduct extends React.Component {
constructor(props) {
super(props);
}
render() {
let { resource ,perpleNum} = this.props;
if(!resource) return null;
let productIcon = resource.productIcon ? getSlicedUrl(resource.productIcon,109*DEVICE_WIDTH_RATIO, 141*DEVICE_WIDTH_RATIO, 2) : '';
let productGroupPrice = resource.productGroupPrice;
let productName = resource.productName;
let productSalePrice = resource.productSalePrice;
let productSkn = resource.productSkn;
return (
<View style={styles.container}>
<YH_Image style={styles.image} url={productIcon}></YH_Image>
<View style={styles.right}>
<View style={styles.titleView}>
<Text style={styles.title} numberOfLines={2}>{productName}</Text>
</View>
<View style={styles.priceView}>
<Text style={styles.iconText}>{perpleNum}人团</Text>
<Text style={styles.price1}>{productGroupPrice}</Text>
</View>
<View style={styles.subPriceView}>
<Text style={styles.subPrice1}>单人购买:</Text>
<DeleteLineText
style={styles.oldPriceContainer}
textStyle={styles.price2}
lineStyle={styles.deleteLine}
text={productSalePrice}
/>
</View>
</View>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let styles = StyleSheet.create({
container: {
width: 300*DEVICE_WIDTH_RATIO,
height: 141*DEVICE_WIDTH_RATIO,
shadowColor: 'rgba(0,0,0,0.4)',
shadowOffset: {width: 0, height: 5},
shadowOpacity: 0.5,
shadowRadius: 5,
elevation: 4,
flexDirection: 'row',
backgroundColor: 'white',
},
image: {
width: 109*DEVICE_WIDTH_RATIO,
height: 141*DEVICE_WIDTH_RATIO,
},
right: {
width: 191*DEVICE_WIDTH_RATIO,
height: 141*DEVICE_WIDTH_RATIO,
flexDirection: 'column',
},
titleView: {
width: 174*DEVICE_WIDTH_RATIO,
height: 32*DEVICE_WIDTH_RATIO,
marginTop: 17*DEVICE_WIDTH_RATIO,
marginLeft: 10*DEVICE_WIDTH_RATIO,
},
title: {
fontSize: 12,
color: '#444444',
lineHeight: 19,
},
subPriceView: {
width: 174*DEVICE_WIDTH_RATIO,
height: 14*DEVICE_WIDTH_RATIO,
marginLeft: 10*DEVICE_WIDTH_RATIO,
marginTop: 5*DEVICE_WIDTH_RATIO,
flexDirection: 'row',
alignItems: 'center',
},
subPrice1: {
fontSize: 10,
color: '#B0B0B0',
lineHeight: 10*DEVICE_WIDTH_RATIO,
},
priceView: {
width: 174*DEVICE_WIDTH_RATIO,
height: 21*DEVICE_WIDTH_RATIO,
marginLeft: 10*DEVICE_WIDTH_RATIO,
marginTop: 33*DEVICE_WIDTH_RATIO,
flexDirection: 'row',
alignItems: 'center',
},
iconText: {
width: 40*DEVICE_WIDTH_RATIO,
height: 14*DEVICE_WIDTH_RATIO,
backgroundColor: '#D0021B',
color: 'white',
fontSize: 9,
textAlign: 'center',
lineHeight: 14*DEVICE_WIDTH_RATIO,
},
price1: {
fontSize: 18,
marginLeft: 10*DEVICE_WIDTH_RATIO,
color: '#444444',
lineHeight: 18*DEVICE_WIDTH_RATIO,
fontWeight: 'bold',
},
oldPriceContainer: {
flexDirection: 'row',
height: 12,
marginTop: 2,
},
price2: {
fontSize: 12,
lineHeight: 12,
color: '#B0B0B0',
alignItems: 'center',
},
});
... ...
... ... @@ -3,7 +3,8 @@
import React, {Component} from 'react';
import {Dimensions, Image, ListView, StyleSheet, Text, TouchableOpacity, View, NativeModules} from 'react-native';
import {Immutable} from 'immutable';
import GroupProductCell from './GroupProductCell'
import GroupProductCell from './GroupProductCell';
import GroupDetailHeader from './GroupDetailHeader';
import YH_Image from '../../common/components/YH_Image';
import {getSlicedUrl} from '../../classify/utils/Utils';
... ... @@ -21,7 +22,17 @@ export default class GroupPurchaseDetail extends Component {
}
_renderHeader() {
return null;
let {
groupDetail,
} = this.props;
return (
<GroupDetailHeader
groupDetail={groupDetail}
updateTime={this.props.updateTime}
didTouchButton={this.props.didTouchButton}
/>
);
}
_renderRow(rowData, sectionID, rowID) {
... ... @@ -96,11 +107,11 @@ let styles = StyleSheet.create({
height: 40*DEVICE_WIDTH_RATIO,
justifyContent: 'center',
alignItems: 'center',
color: '#444444',
},
title: {
fontSize: 16,
fontWeight: 'bold',
color: '#444444',
},
})
;
... ...
'use strict';
import React from 'react';
import Immutable, {Map} from 'immutable';
import YH_Image from '../../common/components/YH_Image';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
export default class HeaderList extends React.Component {
constructor(props) {
super(props);
}
render() {
let { resource ,lackNum} = this.props;
if(!resource) return null;
for (var i = 0; i < lackNum; i++) {
resource.push({
headUrl: '',
})
}
return (
<View style={styles.container}>
{resource.map((item, i) => {
if (i == 0) {
return (
<View key={i} style={styles.icon1}>
<YH_Image style={{width: 40*DEVICE_WIDTH_RATIO,height: 40*DEVICE_WIDTH_RATIO}} url={item.headUrl}></YH_Image>
<Image source={require('../images/PT_tx_tz.png')} style={styles.ovewicon} />
</View>
);
}else {
if (!item.headUrl) {
return (
<Image key={i} source={require('../images/PT_head.png')} style={styles.icon} />
);
}else {
return (
<YH_Image key={i} style={styles.icon} url={item.headUrl}></YH_Image>
);
}
}
})}
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let styles = StyleSheet.create({
container: {
flex: 1,
width: width - 30*DEVICE_WIDTH_RATIO,
height: 40*DEVICE_WIDTH_RATIO,
justifyContent: 'center',
flexDirection: 'row',
},
ovewicon: {
width: 40*DEVICE_WIDTH_RATIO,
height: 40*DEVICE_WIDTH_RATIO,
position: 'absolute',
top: 0,
left: 0,
},
icon1: {
width: 40*DEVICE_WIDTH_RATIO,
height: 40*DEVICE_WIDTH_RATIO,
borderRadius: 20*DEVICE_WIDTH_RATIO,
overflow: 'hidden',
},
icon: {
width: 40*DEVICE_WIDTH_RATIO,
height: 40*DEVICE_WIDTH_RATIO,
borderRadius: 20*DEVICE_WIDTH_RATIO,
overflow: 'hidden',
marginLeft: 20*DEVICE_WIDTH_RATIO,
}
});
... ...
... ... @@ -15,4 +15,7 @@ export default keyMirror({
ACTIVITY_GROUP_DETAIL_REQUEST: null,
ACTIVITY_GROUP_DETAIL_SUCCESS: null,
ACTIVITY_GROUP_DETAIL_FAILURE: null,
UPDATE_LEFTTIME: null,
SHOWSHARVIEW: null,
SHOWSNAPSHOOTSHARE: null,
});
... ...
... ... @@ -37,6 +37,9 @@ class GroupPurchaseDetailContainer extends Component {
constructor(props) {
super(props);
this._onEndReached = this._onEndReached.bind(this);
this.updateTime = this.updateTime.bind(this);
this.didTouchButton = this.didTouchButton.bind(this);
this.showSnapshootShare = this.showSnapshootShare.bind(this);
}
componentDidMount() {
... ... @@ -48,23 +51,52 @@ class GroupPurchaseDetailContainer extends Component {
}
updateTime(leftTime) {
if(leftTime <= 0){
this.props.actions.fetchActivityGroupDetail();
return;
}
this.props.actions.updateTime(leftTime);
}
didTouchButton(resource){
if(pageGo == 1) {
this.props.actions.showShareView(true);
}else if (pageGo == 2) {
}else if (pageGo == 3) {
this.props.actions.showShareView(true);
}else if (pageGo == 4) {
}else if (pageGo == 5) {
}else if (pageGo == 6) {
}else if (pageGo == 7) {
}
}
showSnapshootShare(show){
this.props.actions.showSnapshootShare(show);
}
_onEndReached() {
this.props.actions.getProductList();
}
render() {
let {
showShareView,
showSnapshootShare,
groupDetail,
productList,
} = this.props.groupPurchase;
} = this.props.groupPurchaseDetail;
return (
<View style={styles.container}>
<GroupPurchaseDetail
productList={productList}
groupDetail={groupDetail}
onEndReached={this._onEndReached}
updateTime={this.updateTime}
showShareView={this.showShareView}
/>
</View>
);
}
}
... ...
... ... @@ -11,7 +11,7 @@ let InitialState = Record({
page: 0,
total: 0,
pagetotal: 0,
pageSize: 2,//60,
pageSize: 20,//60,
endReached: false,
})),
resource: new (Record({
... ...
... ... @@ -14,9 +14,24 @@ const {
ACTIVITY_GROUP_DETAIL_REQUEST,
ACTIVITY_GROUP_DETAIL_SUCCESS,
ACTIVITY_GROUP_DETAIL_FAILURE,
UPDATE_LEFTTIME,
SHOWSHARVIEW,
SHOWSNAPSHOOTSHARE,
} = require('../../constants/actionTypes').default;
export function showShareView(show) {
return {
type: SHOWSHARVIEW,
payload: show,
}
}
export function showSnapshootShare(show) {
return {
type: SHOWSNAPSHOOTSHARE,
payload: show,
}
}
export function setGroupNo(groupNo) {
return {
... ... @@ -127,3 +142,10 @@ export function getProductList(reload=false) {
});
};
}
export function updateTime(leftTime) {
return {
type: UPDATE_LEFTTIME,
payload: leftTime
}
}
... ...
... ... @@ -12,9 +12,23 @@ let InitialState = Record({
page: 0,
total: 0,
pagetotal: 0,
pageSize: 2,//60,
pageSize: 20,//60,
endReached: false,
})),
groupDetail: new (Record({
isFetching: false,
error: null,
groupId : 0,
leftTime : 0,//到期时间,秒数
isNewCustomer: 0,// 是否新客
pageGo: 0,// 状态页: 1,开团成功--准备邀请好友参团; 2,尚未加入团--应好友邀请; 3,已经加入团--应好友邀请; 4,团已达成--你已加入一起购买成功; 5,团已达成--你来晚了没能加入; 6,拼团失败--过期未达成
groupRole: 0,// 1,参团; 0,开团
lackNum : 0,// 如团未达成,欠缺人数
openerJoinItem : List(),// 他购买了, status=2,即未参团时有效
yourJoinItem : List(), //你购买了, status=1,即已参团时有效
membershipItems : List(),
joinLimit: 1,
})),
});
export default InitialState;
... ...
... ... @@ -12,7 +12,9 @@ const {
ACTIVITY_GROUP_DETAIL_REQUEST,
ACTIVITY_GROUP_DETAIL_SUCCESS,
ACTIVITY_GROUP_DETAIL_FAILURE,
UPDATE_LEFTTIME,
SHOWSHARVIEW,
SHOWSNAPSHOOTSHARE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
... ... @@ -23,6 +25,12 @@ export default function couponReducer(state = initialState, action) {
}
switch (action.type) {
case SHOWSHARVIEW: {
return state.set('showShareView', action.payload);
}
case SHOWSNAPSHOOTSHARE: {
return state.set('showSnapshootShare', action.payload);
}
case SET_ACTIVITY_ID: {
return state.set('activityId', action.payload);
}
... ... @@ -54,6 +62,45 @@ export default function couponReducer(state = initialState, action) {
return state.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], action.payload);
}
case ACTIVITY_GROUP_DETAIL_REQUEST: {
return state.setIn(['groupDetail', 'isFetching'], true)
.setIn(['groupDetail', 'error'], null);
}
case ACTIVITY_GROUP_DETAIL_SUCCESS: {
let {
groupId,
leftTime,
isNewCustomer,
pageGo,
groupRole,
lackNum,
openerJoinItem,
yourJoinItem,
membershipItems,
joinLimit,
} = action.payload;
return state.setIn(['groupDetail', 'isFetching'], false)
.setIn(['groupDetail', 'error'], null)
.setIn(['groupDetail', 'groupId'], groupId)
.setIn(['groupDetail', 'leftTime'], leftTime)
.setIn(['groupDetail', 'isNewCustomer'], isNewCustomer)
.setIn(['groupDetail', 'pageGo'], pageGo)
.setIn(['groupDetail', 'groupRole'], groupRole)
.setIn(['groupDetail', 'lackNum'], lackNum)
.setIn(['groupDetail', 'openerJoinItem'], Immutable.fromJS(openerJoinItem))
.setIn(['groupDetail', 'yourJoinItem'], Immutable.fromJS(yourJoinItem))
.setIn(['groupDetail', 'membershipItems'], Immutable.fromJS(membershipItems))
.setIn(['groupDetail', 'joinLimit'], joinLimit);
}
case ACTIVITY_GROUP_DETAIL_FAILURE: {
return state.setIn(['groupDetail', 'isFetching'], false)
.setIn(['groupDetail', 'error'], action.payload);
}
case UPDATE_LEFTTIME: {
state.setIn(['groupDetail', 'leftTime'], action.payload);
}
}
return state;
... ...
... ... @@ -26,11 +26,9 @@ export default class groupPurchaseService {
}
})
.then((json) => {
console.log(json);
return json;
})
.catch((error) => {
console.log(error);
throw(error);
});
}
... ... @@ -40,12 +38,11 @@ export default class groupPurchaseService {
url: '',
body: {
method: 'app.activity.groupDetail',
groupNo,
groupNo: '20354618709',//'19649975637', //'19809359189',
uid,
}
})
.then((json) => {
console.log(json);
return json;
})
.catch((error) => {
... ... @@ -64,7 +61,6 @@ export default class groupPurchaseService {
}
})
.then((json) => {
console.log(json);
return json;
})
.catch((error) => {
... ...