Authored by 陈林

Merge branch 'V1130_Date' into 'master'

V1130 date



See merge request !23
Showing 42 changed files with 2146 additions and 36 deletions
... ... @@ -33,6 +33,7 @@ import AssociatorGift from './associatorGift/AssociatorGift'
import UserLogout from './userLogout/UserLogout'
import Alliance from './alliance/Alliance'
import GroupPurchase from './groupPurchase/GroupPurchase'
import Haggle from './haggle/Haggle'
console.disableYellowBox = true
... ... @@ -65,7 +66,8 @@ export default function native(platform) {
UserLogout(platform);
Alliance(platform);
GroupPurchase(platform);
Haggle(platform);
if (Platform.OS === 'ios') {
// Community(platform);
QRCode(platform);
... ...
... ... @@ -113,7 +113,10 @@ export default function native(platform) {
<Provider store={store}>
<ShareDetailContainer
product_skn={this.props.product_skn}
product_id={this.props.product_id}/>
product_id={this.props.product_id}
collage_activity_id={this.props.collage_activity_id}
collage_price={this.props.collage_price}
collage_rebates_amount={this.props.collage_rebates_amount}/>
</Provider>
);
} else if (type === 'recommendProduct') {
... ... @@ -135,7 +138,7 @@ export default function native(platform) {
} else if (type === 'estimateDetails') {
return (
<Provider store={store}>
<EstimateDetailContainer
<EstimateDetailContainer
time_type={this.props.time_type}
tab_type={this.props.tab_type}/>
</Provider>
... ...
... ... @@ -24,6 +24,7 @@ export default class ShareDetail extends Component {
}
_renderBottom(productInfo,isCollect) {
let rebatesPrice = this.props.collage_activity_id && this.props.collage_activity_id != -1 ? this.props.collage_rebates_amount : productInfo.rebatesAmount;
return (
<View style={styles.bottomBar}>
<View style={[styles.favContainer, styles.center]}>
... ... @@ -43,7 +44,7 @@ export default class ShareDetail extends Component {
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.8} onPress={() => this.props.showShareView(productInfo)}>
<View style={[styles.button, styles.center, styles.red]}>
<Text style={styles.btnText}>{'分享可赚¥' + productInfo.rebatesAmount}</Text>
<Text style={styles.btnText}>{'分享可赚¥' + rebatesPrice}</Text>
</View>
</TouchableOpacity>
</View>
... ... @@ -84,6 +85,10 @@ export default class ShareDetail extends Component {
let imgList = goodsImagesList && goodsImagesList.map(item => ({src: item.imageUrl}));
imgList = Immutable.fromJS(imgList);
let activityId = this.props.collage_activity_id;
let salesPrice = activityId && activityId != -1 ? this.props.collage_price : productPriceBo.salesPrice;
let groupWidth = activityId && activityId != -1 ? 50 : 0;
return (
<View>
<View style={[styles.imgContainer]}>
... ... @@ -98,9 +103,10 @@ export default class ShareDetail extends Component {
<View style={styles.priceContainer}>
<View style={styles.alignBottom}>
<Text style={styles.priceUnit}></Text>
<Text style={styles.price}>{productPriceBo.salesPrice}</Text>
<Text style={styles.price}>{salesPrice}</Text>
{productPriceBo.salesPrice !== productPriceBo.marketPrice && <Text style={styles.origpriceUnit}></Text>}
{productPriceBo.salesPrice !== productPriceBo.marketPrice && <Text style={styles.origPrice}>{productPriceBo.marketPrice}</Text>}
<Image source={require('../images/Group3.png')} style={{marginLeft: 10, height: 17, width: groupWidth}} />
</View>
</View>
</View>
... ...
'use strict';
import React, {Component} from 'react';
import {Dimensions, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import {Dimensions, StyleSheet, Text, TouchableOpacity, View, Image} from 'react-native';
import YH_Image from '../../../common/components/YH_Image';
... ... @@ -15,6 +15,11 @@ export default class ProductCell extends Component {
let data = this.props.data;
let prdImage = YH_Image.getSlicedUrl(data.get('default_images', ''), 80, 109, 2);
let yh_exposureData = this.props.yh_exposureData ? this.props.yh_exposureData : null;
let activityId = data.get('collage_activity_id');
let salesPrice = activityId || activityId == 0 ? data.get('collage_price') : data.get('sales_price');
let rebatesPrice = activityId || activityId == 0 ? data.get('collage_rebates_amount') : data.get('rebates_amount');
let groupWidth = activityId || activityId == 0 ? 50 : 0;
return (
<View>
<View style={styles.fatherContainer}>
... ... @@ -27,14 +32,15 @@ export default class ProductCell extends Component {
<YH_Image style={styles.prdImage} url={prdImage}/>
<Text style={styles.prdName} numberOfLines={2}>{data.get('product_name', '')}</Text>
<View style={styles.priceContainer}>
<Text style={styles.nowPrice} numberOfLines={1}>¥{data.get('sales_price')}</Text>
<Text style={styles.nowPrice} numberOfLines={1}>¥{salesPrice}</Text>
<Image source={require('../../images/Group3.png')} style={{marginLeft: 10, height: 17, width: groupWidth}} />
</View>
<View style={[styles.bottomView, styles.returnTextContainer]}>
<Text style={styles.returnText}>最高返</Text>
<Text style={[styles.returnText, {
marginLeft: 4,
fontSize: 16
}]}>¥{data.get('rebates_amount')}</Text>
}]}>¥{rebatesPrice}</Text>
</View>
<View style={styles.shareContainer}>
<Text style={styles.share}>去分享</Text>
... ...
... ... @@ -80,7 +80,6 @@ class HomeContainer extends Component {
json && json.forEach(item => {
if (item.template_name === 'recommendGoodsGroup' && item.data) {
let productPool = item.data.recommendLogic;
console.log('11111111111111111111111111 productPool: ', productPool);
let skns = item.data.skns;
self.props.actions.getProductList(productPool, skns);
let params = {
... ... @@ -129,6 +128,12 @@ class HomeContainer extends Component {
let productSkn = product && product.get('product_skn', 0);
let product_id = product && product.get('product_id', 0);
//拼团需要传递的字段
let collage_activity_id = product && product.get('collage_activity_id', -1);
let collage_price = product && product.get('collage_price', 0);
let collage_rebates_amount = product && product.get('collage_rebates_amount', 0);
if (!productSkn) {
return;
}
... ... @@ -138,7 +143,7 @@ class HomeContainer extends Component {
pageName = 'aFP_Alliance';
}
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.minealliance","params":{"type":"shareDetail","title":"有赚商品详情", "product_skn":"${productSkn}", "product_id": "${product_id}" ,"from_page_name":"${pageName}"}}`;
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.minealliance","params":{"type":"shareDetail","title":"有赚商品详情", "product_skn":"${productSkn}", "product_id": "${product_id}" ,"from_page_name":"${pageName}", "collage_activity_id":"${collage_activity_id}", "collage_price":"${collage_price}", "collage_rebates_amount":"${collage_rebates_amount}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
... ... @@ -171,8 +176,11 @@ class HomeContainer extends Component {
productPool,
skns,
} = this.props.app;
this.props.actions.getProductList(productPool, skns);
console.log('执行');
// _onEndReached 事件会出现在 productPool返回前触发,可能会导致数据不一致,因此控制为0时不调用_onEndReached
if(productPool != 0){
this.props.actions.getProductList(productPool, skns);
}
}
render() {
... ...
... ... @@ -74,6 +74,12 @@ class RecommendContainer extends Component {
onPressProduct(product) {
let productSkn = product && product.get('product_skn', 0);
let product_id = product && product.get('product_id', 0);
//拼团需要传递的字段
let collage_activity_id = product && product.get('collage_activity_id', -1);
let collage_price = product && product.get('collage_price', 0);
let collage_rebates_amount = product && product.get('collage_rebates_amount', 0);
if (!productSkn) {
return;
}
... ... @@ -83,7 +89,7 @@ class RecommendContainer extends Component {
pageName = 'aFP_Alliance';
}
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.minealliance","params":{"type":"shareDetail", "product_skn":"${productSkn}", "product_id": "${product_id}" ,"from_page_name":"${pageName}"}}`;
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.minealliance","params":{"type":"shareDetail", "product_skn":"${productSkn}", "product_id": "${product_id}" ,"from_page_name":"${pageName}", "collage_activity_id": "${collage_activity_id}" ,"collage_price":"${collage_price}", "collage_rebates_amount":"${collage_rebates_amount}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
... ...
... ... @@ -67,6 +67,9 @@ class ShareDetailContainer extends Component {
jumpWithUrl={this._jumpWithUrl}
onEndReached={this._onEndReached}
onPressProduct={this._onPressProduct}
collage_activity_id={this.props.collage_activity_id}
collage_rebates_amount={this.props.collage_rebates_amount}
collage_price={this.props.collage_price}
/>
</View>
)
... ... @@ -87,6 +90,12 @@ class ShareDetailContainer extends Component {
let productSkn = product && product.get('product_skn', 0);
let product_id = product && product.get('product_id', 0);
//拼团需要传递的字段
let collage_activity_id = product && product.get('collage_activity_id', -1);
let collage_price = product && product.get('collage_price', 0);
let collage_rebates_amount = product && product.get('collage_rebates_amount', 0);
if (!productSkn) {
return;
}
... ... @@ -96,7 +105,7 @@ class ShareDetailContainer extends Component {
pageName = 'aFP_Alliance';
}
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.minealliance","params":{"type":"shareDetail","title":"有赚商品详情", "product_skn":"${productSkn}", "product_id": "${product_id}" ,"from_page_name":"${pageName}"}}`;
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.minealliance","params":{"type":"shareDetail","title":"有赚商品详情", "product_skn":"${productSkn}", "product_id": "${product_id}" ,"from_page_name":"${pageName}", "collage_activity_id":"${collage_activity_id}", "collage_price":"${collage_price}", "collage_rebates_amount":"${collage_rebates_amount}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
... ...
... ... @@ -18,6 +18,7 @@ export default class AppService {
body: {
method: 'app.search.cpsPool.productList',
uid,
gender: '',
page,
limit,
msort: msort === 0 ? undefined : msort,
... ...
/*
* @Author: QC.L
* @Date: 2018-09-11 16:07:56
* @Author: QC.L
* @Date: 2018-09-11 16:07:56
* @Last Modified by: QC.L
* @Last Modified time: 2018-09-11 16:14:40
* @Description 弹框 Alert 效果
... ...
... ... @@ -5,7 +5,7 @@
// miniQrType=7 商品详情 √
// miniQrType=9 h5 √
// miniQrType=15 拼团列表 √
// miniQrType=18 逛
// miniQrType=18 逛
// miniQrType=15
... ... @@ -19,8 +19,8 @@ const shareGroupPurchaseList = (shareInfo) => {
let param = {
activityId,
}
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=15';
let miniProgramPath = `/pages/groupPurchase/groupPurchase?activityId=${activityId}`;
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=24' + '&miniapp_type=60';
let miniProgramPath = `/pages/group/list?activityId=${activityId}`;
let fromPage = 'GroupPurchase';
let businessId = 'collage';
... ... @@ -48,13 +48,13 @@ const shareGroupPurchaseDetail = (shareInfo) => {
union_type
} = shareInfo;
let miniProgramPath = `pages/groupPurchase/groupPurchaseDetail?productSkn=${productSkn}&activityId=${activityId}&union_type=${union_type}`
let miniProgramPath = `pages/group/detail?productSkn=${productSkn}&activityId=${activityId}&union_type=${union_type}`
let param = {
productSkn,
activityId,
union_type
};
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=17';
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=24' + '&miniapp_type=60';
let shareParam = {
title: productInfo.productName ? productInfo.productName : '',
... ... @@ -162,9 +162,9 @@ const shareH5 = (shareInfo) => {
return shareParam;
}
export {
shareGroupPurchaseList,
shareGroupPurchaseDetail,
export {
shareGroupPurchaseList,
shareGroupPurchaseDetail,
shareGoodsDetail,
shareStrollDetail,
shareH5,
... ...
... ... @@ -140,7 +140,7 @@ export default class GroupDetailHeader extends React.Component {
tipTitlecolor = '#000000';
}else if (pageGo == 5) {
tipTitle = '你来晚了';
buttonText = '查看更多拼团活动';
buttonText = '我也要开团';
tipTitlecolor = '#000000';
}else if (pageGo == 6) {
tipTitle = '拼团失败';
... ...
... ... @@ -43,6 +43,28 @@ export default class GroupPurchaseDetail extends Component {
switch(sectionID) {
case 'productResourceInfo': {
if(!rowData || !rowData.get('data')){
return null;
}
let infoData = rowData.get('data');
let info = infoData?infoData.toJS():'';
let item = info[0];
let src = YH_Image.getSlicedUrl(item.src, width, 70, 2);
return(
<View style={{width:width, height:70*DEVICE_WIDTH_RATIO+8, backgroundColor:'#f0f0f0'}}>
<View style={{width:width, height:70*DEVICE_WIDTH_RATIO}}>
<TouchableOpacity activeOpacity={1} onPress={() => {
this.props.didTouchBanner && this.props.didTouchBanner(item.url);
}}>
<YH_Image style={{width: '100%', height: '100%'}} url={src}></YH_Image>
</TouchableOpacity>
</View>
</View>
);
}
case 'productListTitle': {
return(
<View style={styles.productListheader}>
... ... @@ -52,6 +74,7 @@ export default class GroupPurchaseDetail extends Component {
</View>
);
}
case 'productList': {
return(
<GroupProductCell resource={rowData} didTouchProduct={this.props.didTouchProduct}/>
... ... @@ -65,9 +88,11 @@ export default class GroupPurchaseDetail extends Component {
render() {
let {
productList,
resource,
} = this.props;
let dataSource = {
productListTitle: [1],
productResourceInfo: resource.resourceList ? resource.resourceList.toArray() : [],
productListTitle: [2],
productList: productList.list ? productList.list.toArray() : [],
};
... ...
... ... @@ -118,7 +118,7 @@ class GroupPurchaseContainer extends Component {
if (!resource) {
return;
}
let miniProgramPath = '/pages/groupPurchase/groupPurchase?activityId=' + activityId;
let miniProgramPath = '/pages/group/list?activityId=' + activityId;
let fromPage = 'GroupPurchase';
let bigImage = shareCodeInfo.get('bigImage');
let productIcon = bigImage ? getSlicedUrl(bigImage,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
... ... @@ -164,7 +164,7 @@ class GroupPurchaseContainer extends Component {
activityId,
}
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=15';
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=24' + '&miniapp_type=60';
return (
<View style={styles.container}>
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {Platform, StyleSheet, View,Dimensions,} from 'react-native'
import ReactNative, {Platform, StyleSheet, View, Dimensions, NativeAppEventEmitter} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
... ... @@ -42,6 +42,7 @@ class GroupPurchaseDetailContainer extends Component {
constructor(props) {
super(props);
this._onEndReached = this._onEndReached.bind(this);
this._didTouchBanner = this._didTouchBanner.bind(this);
this.updateTime = this.updateTime.bind(this);
this.didTouchProduct = this.didTouchProduct.bind(this);
this.goComment = this.goComment.bind(this);
... ... @@ -51,15 +52,22 @@ class GroupPurchaseDetailContainer extends Component {
this.showSnapshootShare = this.showSnapshootShare.bind(this);
this.shareSnapshootAction = this.shareSnapshootAction.bind(this);
this.subscription = NativeAppEventEmitter.addListener(
'ShareCollageDetailEvent',
() => {
this.props.actions.showShareView(true);
}
);
}
async componentDidMount() {
this.props.actions.getProductList();
this.props.actions.fetchActivityGroupDetail();
this.props.actions.fetchResourceInfo();
}
componentWillUnmount() {
this.subscription && this.subscription.remove();
}
updateTime(leftTime) {
... ... @@ -74,7 +82,7 @@ class GroupPurchaseDetailContainer extends Component {
let { activityId, groupNo, groupDetail } = this.props.groupPurchaseDetail;
if(pageGo == 1) {
this.props.actions.showShareView(true);
}else if (pageGo == 2) {
}else if (pageGo == 2 || pageGo == 5) {
let membershipItems = groupDetail.get('membershipItems');
let productDetail = membershipItems.size > 0 ? membershipItems.get(0) : new Map();
let productSkn = productDetail.get('productSkn');
... ... @@ -91,7 +99,7 @@ class GroupPurchaseDetailContainer extends Component {
ReactNative.NativeModules.YH_CommonHelper.showBuyPicker(params);
}else if (pageGo == 3) {
this.props.actions.showShareView(true);
}else if (pageGo == 4 || pageGo == 5 || pageGo == 7 || pageGo == 6) {
}else if (pageGo == 4 || pageGo == 7 || pageGo == 6) {
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.activitytemplate","params":{"type":"2", "activityId":"${activityId}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
... ... @@ -109,6 +117,10 @@ class GroupPurchaseDetailContainer extends Component {
this.props.actions.showSnapshootShare(false);
}
showShareView(show){
this.props.actions.showShareView(show);
}
didTouchProduct(productSkn,activityId,newSrc) {
if(newSrc){
let arr=newSrc.split("?");
... ... @@ -123,8 +135,15 @@ class GroupPurchaseDetailContainer extends Component {
ReactNative.NativeModules.YH_CommonHelper.goLinkUrl(url);
}
showShareView(show){
this.props.actions.showShareView(show);
_didTouchBanner(url) {
if (!url) {
return;
}
let params = {
URL: url,
};
ReactNative.NativeModules.YH_CommonHelper.logEvent('YB_GROUP_DEATIL_BANNER_C', params);
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
async shareMiniApp(){
... ... @@ -145,7 +164,7 @@ class GroupPurchaseDetailContainer extends Component {
return;
}
let productIcon = resource.productIcon ? getSlicedUrl(resource.productIcon,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let miniProgramPath = '/pages/groupPurchase/groupPurchaseResult?activity_id=' + activityId + '&group_no=' + groupNo;
let miniProgramPath = '/pages/group/result?activity_id=' + activityId + '&group_no=' + groupNo;
unionType && (miniProgramPath += '&unionType=' + unionType);
let productGroupPrice = resource.productGroupPrice;
let productName = resource.productName;
... ... @@ -182,6 +201,7 @@ class GroupPurchaseDetailContainer extends Component {
showSnapshootShare,
groupDetail,
productList,
resourceInfo
} = this.props.groupPurchaseDetail;
let {
... ... @@ -194,7 +214,7 @@ class GroupPurchaseDetailContainer extends Component {
group_no: groupNo,
}
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=4';
let qrCode = host + '/wechat/miniapp/img-check.jpg?param=' + encodeURIComponent(JSON.stringify(param)) + '&miniQrType=26' + '&miniapp_type=60';
return (
<View style={styles.container}>
... ... @@ -211,11 +231,13 @@ class GroupPurchaseDetailContainer extends Component {
activityId={activityId}
productList={productList}
groupDetail={groupDetail}
resource={resourceInfo}
onEndReached={this._onEndReached}
updateTime={this.updateTime}
didTouchButton={this.didTouchButton}
didTouchProduct={this.didTouchProduct}
goComment={this.goComment}
didTouchBanner={this._didTouchBanner}
/>
</View>
);
... ...
... ... @@ -11,6 +11,9 @@ const {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
RESOURCEINFO_REQUEST,
RESOURCEINFO_SUCCESS,
RESOURCEINFO_FAILURE,
ACTIVITY_GROUP_DETAIL_REQUEST,
ACTIVITY_GROUP_DETAIL_SUCCESS,
ACTIVITY_GROUP_DETAIL_FAILURE,
... ... @@ -65,6 +68,26 @@ export function productListFailure(error) {
}
}
export function resourceInfoRequest() {
return {
type: RESOURCEINFO_REQUEST,
};
}
export function resourceInfoSuccess(json) {
return {
type: RESOURCEINFO_SUCCESS,
payload: json
}
}
export function resourceInfoFailure(error) {
return {
type: RESOURCEINFO_FAILURE,
payload: error
}
}
export function activityGroupDetailRequest() {
return {
type: ACTIVITY_GROUP_DETAIL_REQUEST,
... ... @@ -157,3 +180,18 @@ export function updateTime(leftTime) {
payload: leftTime
}
}
export function fetchResourceInfo() {
return (dispatch, getState) => {
let {app} = getState();
dispatch(resourceInfoRequest());
let content_code = 'cea0efae77f4e04c935beb1e87181247';
return new GroupPurchaseService(app.host).fetchDetailResourceInfo(content_code)
.then(json => {
dispatch(resourceInfoSuccess(json));
})
.catch(error => {
dispatch(resourceInfoFailure(error));
});
};
}
... ...
... ... @@ -17,6 +17,11 @@ let InitialState = Record({
pageSize: 20,//60,
endReached: false,
})),
resourceInfo: new (Record({
isFetching: false,
error: null,
resourceList: List(),
})),
groupDetail: new (Record({
isFetching: false,
error: null,
... ...
... ... @@ -9,6 +9,9 @@ const {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
RESOURCEINFO_REQUEST,
RESOURCEINFO_SUCCESS,
RESOURCEINFO_FAILURE,
ACTIVITY_GROUP_DETAIL_REQUEST,
ACTIVITY_GROUP_DETAIL_SUCCESS,
ACTIVITY_GROUP_DETAIL_FAILURE,
... ... @@ -62,6 +65,21 @@ export default function couponReducer(state = initialState, action) {
return state.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], action.payload);
}
case RESOURCEINFO_REQUEST: {
return state.setIn(['resourceInfo', 'isFetching'], true)
.setIn(['resourceInfo', 'error'], null);
}
case RESOURCEINFO_SUCCESS: {
return state.setIn(['resourceInfo', 'isFetching'], false)
.setIn(['resourceInfo', 'resourceList'], Immutable.fromJS(action.payload))
.setIn(['resourceInfo', 'error'], null);
}
case RESOURCEINFO_FAILURE: {
return state.setIn(['resourceInfo', 'isFetching'], false)
.setIn(['resourceInfo', 'error'], action.payload);
}
case ACTIVITY_GROUP_DETAIL_REQUEST: {
return state.setIn(['groupDetail', 'isFetching'], true)
.setIn(['groupDetail', 'error'], null);
... ...
... ... @@ -68,6 +68,23 @@ export default class groupPurchaseService {
});
}
async fetchDetailResourceInfo(content_code) {
let fromPage = Platform.OS === 'android' ? 'aFP_GroupPurchaseDetail' : 'iFP_GroupPurchaseDetail';
return await this.api.get({
url: '/operations/api/v5/resource/get',
body: {
content_code,
fromPage,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async fetchShareCodeInfo(params) {
return await this.api.get({
url: '/operations/api/v5/webshare/getShare',
... ...
'use strict';
import React from 'react';
import {AppRegistry} from 'react-native';
import createReactClass from 'create-react-class';
import {Provider} from 'react-redux';
import configureStore from './store/configureStore';
import haggleInitialState from './reducers/haggleListReducer/haggleInitialState';
import appInitialState from './reducers/app/appInitialState';
import HaggleListContainer from './containers/HaggleListContainer';
import {setHost, setPlatform, setServiceHost} from './reducers/app/appActions';
function getInitialState() {
const _initState = {
app: (new appInitialState()),
haggle: (new haggleInitialState()),
};
return _initState;
}
export default function native(platform) {
let YH_HaggleProductList = createReactClass({
render() {
const store = configureStore(getInitialState());
store.dispatch(setPlatform(platform));
store.dispatch(setHost(this.props.host));
store.dispatch(setServiceHost(this.props.serviceHost));
let type = this.props.type;
return (
<Provider store={store}>
<HaggleListContainer/>
</Provider>
);
}
});
AppRegistry.registerComponent('YH_HaggleProductList', () => YH_HaggleProductList);
}
... ...
'use strict';
import React, { Component } from 'react';
import { Platform, Dimensions, ListView, Image, StyleSheet, View ,Text, TouchableOpacity} from 'react-native';
import { Immutable } from 'immutable';
import TimerMixin from 'react-timer-mixin';
import ProductCell from './cell/productCell';
import Focus from './floor/Focus';
import YH_PtrRefresh from '../../common/components/YH_PtrRefresh';
const DEVICE_WIDTH_RATIO = Dimensions.get('window').width / 375;
export default class HaggleList extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this._changeCategory = this._changeCategory.bind(this);
this._trigggePullToRefresh = this._trigggePullToRefresh.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
}
componentDidMount() {
this._trigggePullToRefresh();
}
_trigggePullToRefresh() {
let {
categoryType,
} = this.props;
}
componentWillUnmount() {
this.timer && TimerMixin.clearTimeout(this.timer);
}
_renderResourceInfoListRow(item) {
if (item.get('template_name') === 'focus' && item.get('data')) {
return (
<View style={styles.topImageView}>
<Focus
data={item.get('data')}
height={121}
resourceJumpWithUrl={this.props.resourceJumpWithUrl}
/>
</View>
);
}
return null;
}
_renderRow(rowData, sectionID, rowID) {
let {
categoryType,
} = this.props;
if(sectionID === 'resourceInfoList'){
return this._renderResourceInfoListRow(rowData)
}else if (sectionID === 'productList') {
return (
<ProductCell
key={'row' + rowID}
rowID={rowID}
data={rowData}
onPressProduct={this.props.onPressProduct}
firstHaggle={this.props.firstHaggle}
continueHaggle={this.props.continueHaggle}
onStop = {this.props.onStop}
categoryType={categoryType}
/>
);
}else {
return null;
}
}
_changeCategory(index) {
let {
categoryType,
} = this.props;
if (index == categoryType) {
return
}
this.props.onPressCategory && this.props.onPressCategory(index);
}
render() {
let {
data,
categoryType,
resourceInfo,
} = this.props;
let productList = data.list ? data.list.toArray() : [];
let color = categoryType == 0 ? '#444444' : '#b0b0b0';
let subcolor = categoryType == 1 ? '#444444' : '#b0b0b0';
let dataSource = null;
if (categoryType == 0) {
dataSource = {
resourceInfoList: resourceInfo.resourceList ? resourceInfo.resourceList.toArray() : [],
productList: data.list ? data.list.toArray() : [],
}
}else {
dataSource = {
productList: data.list ? data.list.toArray() : [],
}
}
return (
<View style={styles.container}>
{
Platform.OS === 'ios' ?
<ListView
ref={(c) => {
this.listView = c;
}}
enableEmptySections={true}
enablePullToRefresh={true}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
renderFooter={this._renderFooter}
isOnPullToRefresh={data.isPullToRefresh}
onEndReachedThreshold ={50}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh(categoryType);
}}
onEndReached={() => {
if (productList.size !== 0) {
this.props.onEndReached && this.props.onEndReached(categoryType);
}
}}
/>
:
<ListView
ref={(c) => {
this.listView = c;
}}
enableEmptySections={true}
enablePullToRefresh={true}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
renderFooter={this._renderFooter}
isOnPullToRefresh={data.isPullToRefresh}
onEndReachedThreshold ={10}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh(categoryType);
}}
refreshControl={
<YH_PtrRefresh
refreshing={data.isPullToRefresh}
onRefresh={() => {
this.props.onRefresh && this.props.onRefresh(categoryType);
}}
colors={['#000000', '#ff0000']}
progressBackgroundColor="#ffffff"
/>
}
/>
}
<View style={styles.bottomView}>
<TouchableOpacity activeOpacity={1} style={styles.textItemStyle} onPress={() => {
this._changeCategory && this._changeCategory(0);
}}>
<Text style={[styles.textsStyle, {color: color}]}>砍价商品</Text>
</TouchableOpacity>
<View style={styles.separator}/>
<TouchableOpacity activeOpacity={1} style={styles.textItemStyle} onPress={() => {
this._changeCategory && this._changeCategory(1);
}}>
<Text style={[styles.textsStyle, {color : subcolor}]}>我的砍价</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
let {width} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
},
topImageView: {
width: width,
height: 121 * DEVICE_WIDTH_RATIO,
},
contentContainer: {
flexDirection: 'column',
backgroundColor: 'white',
paddingBottom: 50 * DEVICE_WIDTH_RATIO,
},
bottomView: {
position: 'absolute',
width: width,
height: 50 * DEVICE_WIDTH_RATIO,
bottom: 0,
flexDirection: 'row',
backgroundColor: 'white',
borderTopColor: '#e0e0e0',
borderTopWidth: 1,
},
textItemStyle: {
width: width/2,
height: 50 * DEVICE_WIDTH_RATIO,
justifyContent:'center'
},
textsStyle: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 16,
textAlign:'center'
},
separator: {
width:1,
height: 50 * DEVICE_WIDTH_RATIO,
backgroundColor: '#f0f0f0'
},
topLineSeparator: {
width:width,
height: 1,
bottom: 55,
backgroundColor: '#f0f0f0'
},
})
;
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import YH_Swiper from '../../../common/components/YH_Swiper';
import YH_Image from '../../../common/components/YH_Image';
import Immutable from 'immutable';
const {
View,
Image,
TouchableOpacity,
StyleSheet,
Dimensions,
Platform,
} = ReactNative;
export default class ImageSlider extends React.Component {
constructor(props) {
super(props);
this._handleParamsJumpWithUrl = this._handleParamsJumpWithUrl.bind(this);
}
shouldComponentUpdate(nextProps) {
if (Immutable.is(nextProps.resource, this.props.resource)
&& nextProps.sliderWidth == this.props.sliderWidth
&& nextProps.sliderHeight == this.props.sliderHeight) {
return false;
} else {
return true;
}
}
_handleParamsJumpWithUrl(index, url) {
// 为埋点提供参数组装
// 后期请将所有的点击事件由 resourceJumpWithUrl 修改为自己独有的事件
// 由于多数组件都使用的 resourceJumpWithUrl ,埋点传参无法正常进行
let params = {
I_INDEX: index,
F_URL: url
};
this.props.resourceJumpWithUrl && this.props.resourceJumpWithUrl(url, 'banner', params);
}
render() {
let {resource, sliderWidth, sliderHeight} = this.props;
if (!resource || resource.size == 0) {
return null;
}
let data = this.props.resource.toJS();
if (data.length == 1) {
let imageUrl = YH_Image.getSlicedUrl(data[0].src, sliderWidth, sliderHeight, 2);
return (
<TouchableOpacity
activeOpacity={1}
yh_exposureData={data[0].yh_exposureData}
style={{width: sliderWidth, height: sliderHeight}}
onPress={() => {
this._handleParamsJumpWithUrl(1, data[0].url)
}}
>
<YH_Image
url={imageUrl}
style={{width: sliderWidth, height: sliderHeight}}
/>
</TouchableOpacity>
);
} else {
return (
<YH_Swiper
showsButtons={false}
loop={true}
autoplay={true}
autoplayTimeout={8}
width={sliderWidth}
height={sliderHeight}
paginationStyle={{
backgroundColor: 'rgba(68, 68, 68, 0.2)',
bottom: 10,
height: 10,
width: data.length * 12,
borderRadius: 5,
marginLeft: (sliderWidth - data.length * 12) / 2,
paddingBottom: 0,
}}
>
{data.map((item, i) => {
let imageUrl = YH_Image.getSlicedUrl(item.src, sliderWidth, sliderHeight, 2);
return (
<TouchableOpacity
key={i}
activeOpacity={1}
yh_exposureData={item.yh_exposureData}
onPress={() => {
this._handleParamsJumpWithUrl(i + 1, item.url);
}}
>
<YH_Image
url={imageUrl}
style={{width: sliderWidth, height: sliderHeight}}
/>
</TouchableOpacity>
);
})}
</YH_Swiper>
);
}
}
}
... ...
'use strict';
import React from 'react';
import Immutable, {Map} from 'immutable';
import YH_Image from '../../../common/components/YH_Image';
import TimerMixin from 'react-timer-mixin';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
export default class TimerLable extends React.Component {
constructor(props) {
super(props);
this.state = {
leftTime: this.props.leftTime,
}
}
componentDidMount() {
this.startTimer();
}
componentWillUnmount() {
this.stopTimer();
}
startTimer() {
this.stopTimer();
if(this.state.leftTime > 0){
this.timer = TimerMixin.setInterval(() => {
let leftTime = this.state.leftTime-1;
if (leftTime < 0) {
this.props.onStop && this.props.onStop()
this.stopTimer();
}
this.setState({
leftTime,
});
}, 1000);
}else {
this.setState({
leftTime: 0,
});
}
}
stopTimer() {
console.log('stopTimer');
this.timer && TimerMixin.clearTimeout(this.timer);
}
format(m) {
if (m < 10){
return 0+''+m;
}
return m+'';
}
formatTime(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);
return hrFormat + ':' + minFormat + ':' + secFormat;
}
render() {
if(this.state.leftTime > 0){
return (
<View style={[styles.container,this.props.style ? this.props.style : {}]}>
<Text style={styles.time}>
{this.formatTime(this.state.leftTime)}
</Text>
</View>
);
} else {
return null;
}
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let leftOffset = 20 * DEVICE_WIDTH_RATIO;
let styles = StyleSheet.create({
container: {
height: 80 * DEVICE_WIDTH_RATIO,
width: 100* DEVICE_WIDTH_RATIO,
flexDirection: 'row',
alignItems: 'center',
},
timeIcon: {
width: 16 * DEVICE_WIDTH_RATIO,
height: 16 * DEVICE_WIDTH_RATIO,
},
time: {
fontFamily: 'Alte DIN 1451 Mittelschrift',
fontSize: 12,
color: '#ffffff',
letterSpacing: 0,
marginLeft: 5,
},
});
... ...
/*
* @Author: QC.L
* @Date: 2018-09-11 16:07:56
* @Last Modified by: QC.L
* @Last Modified time: 2018-09-11 16:14:40
* @Description 弹框 Alert 效果
*/
'use strict';
import React from 'react';
import ReactNative from 'react-native';
const {
StyleSheet,
Text,
View,
Dimensions,
TouchableOpacity,
Modal,
} = ReactNative;
import PropTypes from 'prop-types';
/**
* 参数1: showStatus 值 sure/cancel
* 参数2: handleAction 要处理的事件
*/
function YHAlertItem(props) {
return (
<TouchableOpacity style={styles.click} onPress={() => {
props.handleAction && props.handleAction(props.param ? props.param: {});
}}>
<Text style={styles[props.showStatus]}>{props.children}</Text>
</TouchableOpacity>
);
}
/**
* 参数1: isShow true 显示 false 隐藏
*/
class YHAlert extends React.Component {
static propTypes = {
show: PropTypes.func, // 显示
hide: PropTypes.func, // 隐藏
};
constructor(props) {
super(props);
this._renderChildren = this._renderChildren.bind(this);
}
static defaultProps = {
confirmTitle:{},
};
_renderChildren(children) {
if('[object Array]' == Object.prototype.toString.call(children)){
return children.map((element, index) => {
if (index === children.length) return element
return (
<React.Fragment>
{element}
<View style={{width: 0.5, height: '100%', backgroundColor: '#e0e0e0'}}></View>
</React.Fragment>
)
});
}else {
return children
}
}
render() {
let { children } = this.props;
return (
<Modal
visible={this.props.isShow}
animationType={'none'}
transparent={true}
onRequestClose={() => {}}>
<View style={styles.modalContainer}>
<View style={styles.modalView}>
{this.props.title ? <View style={styles.confirmTitleContainer}>
<Text style={[styles.confirmTitle,this.props.titleStyle]} numberOfLines={1}>{this.props.title}</Text>
</View>: null}
{this.props.content ? <View style={styles.confirmContentContainer}>
<Text style={[styles.confirmContent,this.props.contentStyle]}numberOfLines={3}>{this.props.content}</Text>
</View>: null}
<View style={{width: '100%', height: 0.5, backgroundColor: '#e0e0e0'}}/>
<View style={styles.confirmBtnContainer}>
{this._renderChildren(children)}
</View>
</View>
</View>
</Modal>
);
}
};
export { YHAlert, YHAlertItem };
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
modalContainer: {
flex: 1,
width: width,
height: height,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.4)',
},
modalView: {
width: 270,
alignItems: 'center',
backgroundColor: '#ffffff',
borderRadius : 5
},
confirmBtnContainer: {
width: '100%',
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
},
confirmTitleContainer: {
alignItems: 'center'
},
confirmTitle: {
paddingTop: 20,
paddingLeft: 30,
paddingRight: 30,
paddingBottom: 10,
color: '#000000',
fontSize: 16,
lineHeight: 24,
textAlign: 'center',
letterSpacing: -0.09,
fontFamily: 'PingFang-SC-Regular',
fontWeight: '600',
},
confirmContentContainer: {
alignItems: 'center'
},
confirmContent: {
paddingLeft: 30,
paddingRight: 30,
paddingBottom: 20,
color: '#999999',
fontSize: 14,
lineHeight: 24,
textAlign: 'center',
letterSpacing: -0.09,
fontFamily: 'PingFang-SC-Regular',
},
click: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
cancel: {
fontSize: 17,
color: '#000000',
letterSpacing: -0.41,
fontFamily: 'PingFang-SC-Regular',
fontWeight: 'bold',
},
sure: {
fontSize: 17,
color: '#999999',
letterSpacing: -0.41,
},
blackButton: {
fontSize: 17,
color: 'black',
letterSpacing: -0.41,
},
LightGrayButton: {
fontSize: 17,
color: '#999999',
letterSpacing: -0.41,
fontFamily: 'PingFang-SC-Regular',
},
redButton: {
fontSize: 17,
color: 'red',
letterSpacing: -0.41,
fontFamily: 'PingFang-SC-Regular',
},
});
... ...
'use strict';
import React, {Component} from 'react';
import {Dimensions, StyleSheet, Text, TouchableOpacity, View, ImageBackground,Image} from 'react-native';
import YH_Image from '../../../common/components/YH_Image';
import TimerLabel from './TimerLable';
export default class productCell extends Component {
constructor(props) {
super(props);
this._topTimerView = this._topTimerView.bind(this);
this._priceView = this._priceView.bind(this);
this._cutActionView = this._cutActionView.bind(this);
}
_topTimerView(data) {
let leftTime = data.get('cutEndTime') - data.get('currentTime');
let status = data.get('cutStatus');
let des = '后结束';
if (status == 2) {
des = '已成功';
}else if (status == 3){
des = '已结束';
}
return (
<View style= {styles.timeFatherContainer}>
<ImageBackground source={require('../../images/timeBg.png')} style={styles.timeBg}>
</ImageBackground>
<View style={styles.timeContainer}>
{data.get('cutStatus')==1? <TimerLabel leftTime = {leftTime} style={styles.timeCount} onStop = {this.props.onStop}/>:null }
<Text style={styles.timeDes}>{des}</Text>
</View>
</View>
);
}
_priceView(data) {
let status = data.get('cutStatus');
if (status==2 || status==3) {
return null;
}else {
return (
<View>
<View style={styles.priceFatherContainer}>
<View style={styles.nowPriceContainer}>
<Text style={styles.nowPrice}>¥</Text>
<Text style={[styles.nowPrice, {
marginLeft: 1,
marginTop:-4,
fontSize: 18
}]}>{data && data.get('highPrice')}</Text>
</View>
{data.get('hasCutPrice') ?
<View style={styles.cutPriceContainer}>
<Text style={styles.hasCutPrice}>已砍¥</Text>
<Text style={[styles.hasCutPrice, {
marginLeft: 1,
}]}>{data && data.get('hasCutPrice')}</Text>
</View>
: null }
</View>
</View>
);
}
}
_cutActionView(data) {
let categoryType = this.props.categoryType;
if (categoryType == 1) {
if (data.get('cutStatus') == 1) {
return (
<View style={styles.actionContainer}>
<View style={styles.continueCutContainer}>
<Text style={styles.continueCut}>继续砍价</Text>
</View>
</View>
)
}else if (data.get('cutStatus') == 2) {
return (
<View style={styles.actionContainer}>
<View style={styles.continueCutContainer}>
<Text style={styles.continueCut}>立即购买</Text>
</View>
</View>
)
}else {
return null;
}
}else {
return (
<View style={styles.actionContainer}>
<View style={styles.cutActionContainer}>
<Image source={require('../../images/cutPriceBg.png')} style={styles.cutPriceBg}></Image>
<Text style={styles.cutPrice}>-¥{data && data.get('lowPrice')}</Text>
</View>
</View>
)
}
}
render() {
let data = this.props.data;
let prdImage = YH_Image.getSlicedUrl(data && data.get('defaultImages', ''), 98, 130, 2);
return (
<View>
<View style={styles.fatherContainer}>
{data.get('cutStatus')?this._topTimerView(data):null}
<TouchableOpacity
activeOpacity={1} style={[styles.container]}
onPress={() => {
this.props.onPressProduct && this.props.onPressProduct(data);
}}>
<View style={styles.container}>
<YH_Image style={styles.prdImage} url={prdImage}/>
<View style={styles.rightContainer}>
<Text style={styles.prdName} numberOfLines={2}>{data && data.get('productName', '')}</Text>
{this._priceView(data)}
</View>
{this._cutActionView(data)}
</View>
</TouchableOpacity>
</View>
<View style={styles.separator}/>
</View>
);
}
};
let {width} = Dimensions.get('window');
let nameWidth = width - 15 - 98 - 15 - 10;
let styles = StyleSheet.create({
fatherContainer: {
backgroundColor: 'white',
padding: 15,
},
container: {
flexDirection: 'row',
width: width - 30,
},
rightContainer: {
justifyContent: 'space-between',
},
prdImage: {
marginTop: 5,
width: 98,
height: 130,
},
prdName: {
fontFamily: 'PingFang-SC-Light',
fontSize: 14,
color: '#444444',
marginTop: 10,
marginLeft: 10,
width: nameWidth,
letterSpacing: -0.34,
},
timeFatherContainer: {
flexDirection: 'row',
},
priceFatherContainer: {
flex:1,
marginBottom: 0,
marginLeft: 10,
},
nowPriceContainer: {
flexDirection: 'row',
},
cutPriceContainer:{
flexDirection: 'row',
},
nowPrice: {
fontSize: 14,
fontFamily: 'PingFang-SC-Medium',
color: '#222222',
},
hasCutPrice: {
fontSize: 14,
fontFamily: 'PingFang-SC-Medium',
color: '#d0021b',
},
cutActionContainer: {
width: 91,
height: 33,
justifyContent: 'center',
},
cutPriceBg: {
width: 91,
height: 33,
},
cutPrice: {
fontSize: 14,
fontFamily: 'PingFang-SC-Medium',
color: 'white',
marginTop:-25,
marginRight: 10,
textAlign: 'right',
},
continueCutContainer: {
width: 91,
height: 33,
borderRadius: 3,
backgroundColor: '#D0021B',
justifyContent: 'center',
},
continueCut: {
fontSize: 14,
color: 'white',
textAlign: 'center',
},
separator: {
width:width-30,
marginLeft: 15,
height: 1,
backgroundColor: '#f0f0f0'
},
timeBg:{
justifyContent: 'center',
width: 21,
height: 31,
marginLeft:-3,
},
timeContainer :{
alignItems: 'center',
flexDirection: 'row',
marginLeft:-2,
backgroundColor: '#222222',
height: 24,
},
timeIcon:{
width: 9,
height: 10,
marginLeft:3,
},
timeCount:{
width:55,
marginLeft: 4,
},
timeDes:{
fontSize: 12,
color: 'white',
marginLeft:3,
marginRight:10,
fontFamily: 'PingFang-SC-Medium',
},
invalidDes: {
fontSize: 14,
fontFamily: 'PingFang-SC-Medium',
color: '#b0b0b0',
},
actionContainer: {
position: 'absolute',
right: 0,
width: 91,
height: 33,
bottom: 0,
}
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import ImageSlider from '../cell/ImageSlider';
import Immutable from 'immutable';
const {
StyleSheet,
Dimensions,
View,
} = ReactNative;
export default class Focus extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps) {
if (Immutable.is(nextProps.data, this.props.data)) {
return false;
} else {
return true;
}
}
render() {
let {data} = this.props;
let sliderHeight = Math.ceil(this.props.height * DEVICE_WIDTH_RATIO);
return (
<View style={{height:sliderHeight, width:width}}>
<ImageSlider
resource={data}
sliderWidth={width}
sliderHeight={sliderHeight}
resourceJumpWithUrl={this.props.resourceJumpWithUrl}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let styles = StyleSheet.create({
banner: {},
});
... ...
import keyMirror from 'key-mirror';
export default keyMirror({
SET_PLATFORM: null,
SET_CHANNEL: null,
SET_HOST: null,
SET_SERVICE_HOST: null,
PRODUCT_LIST_REQUEST: null,
PRODUCT_LIST_SUCCESS: null,
PRODUCT_LIST_FAILURE: null,
MYHAGGLE_PRODUCT_LIST_REQUEST: null,
MYHAGGLE_PRODUCT_LIST_SUCCESS: null,
MYHAGGLE_PRODUCT_LIST_FAILURE: null,
RESOURCE_INFO_REQUEST: null,
RESOURCE_INFO_SUCCESS: null,
RESOURCE_INFO_FAILURE: null,
SET_SELECTED_CATEGORY: null,
REFRESH_PRODUCT_LIST:null,
HAGGLE_STATUS_SUCCESS:null,
SHOW_HASCUT_ALERT:null,
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {AsyncStorage, Platform, StyleSheet, View, Dimensions, InteractionManager} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {Map} from 'immutable';
import * as allianceActions from '../reducers/haggleListReducer/haggleActions';
import HaggleList from '../components/HaggleList'
import LoadingIndicator from '../../common/components/LoadingIndicator';
import { TipsAlertItem, TipsAlert } from '../../common/components/YH_TipsAlert';
import { YHAlert, YHAlertItem } from '../components/cell/YH_Alert.js';
const actions = [
allianceActions,
];
function mapStateToProps(state) {
return {
...state
};
}
function mapDispatchToProps(dispatch) {
const creators = Map()
.merge(...actions)
.filter(value => typeof value === 'function')
.toObject();
return {
actions: bindActionCreators(creators, dispatch),
dispatch
};
}
class HaggleListContainer extends Component {
constructor(props) {
super(props);
this.state = {showAlert:false, productItem:null};
this._onEndReached = this._onEndReached.bind(this);
this._onPressProduct = this._onPressProduct.bind(this);
this._jumpHaggleDetail = this._jumpHaggleDetail.bind(this);
this._onPressCategory = this._onPressCategory.bind(this);
this._hiddenTipsAlertDialog = this._hiddenTipsAlertDialog.bind(this);
this._timerStop = this._timerStop.bind(this);
this._resourceJumpWithUrl = this._resourceJumpWithUrl.bind(this);
this._onRefresh = this._onRefresh.bind(this);
}
componentDidMount() {
this.props.actions.getResourceInfo(function (json) {
json && json.forEach(item => {
})
});
this.props.actions.getHaggleProductList();
}
componentWillUnmount() {
}
_resourceJumpWithUrl(url, type, params) {
if (!url) {
return;
}
if (type === 'icon' && params) {
ReactNative.NativeModules.YH_CommonHelper.logEvent('YB_CPS_MAIN_ICON_C', params);
} else if (type === 'banner' && params) {
ReactNative.NativeModules.YH_CommonHelper.logEvent('YB_CPS_MAIN_BANNER_C', params);
} else if (type && params) {
ReactNative.NativeModules.YH_CommonHelper.logEvent('YB_CPS_MAIN_ACTIVITY_C', params);
}
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onEndReached(categoryType) {
if (categoryType == 0) {
this.props.actions.getHaggleProductList();
}else if (categoryType == 1){
this.props.actions.getMyHaggleProductList();
}
}
_onRefresh(categoryType) {
InteractionManager.runAfterInteractions(() => {
this.props.actions.refreshProductList(categoryType);
});
}
_timerStop() {
this.props.actions.refreshProductList(1);
}
_showTipsAlertDialog(){
this.props.actions.showTipsAlertDialog();
}
_hiddenTipsAlertDialog(){
this.setState({showAlert:false, productItem:null});
}
_onPressCategory(index) {
this.props.actions.setSelectedCategory(index);
}
_jumpHaggleDetail(product, status) {
let productSkn = product && product.get('productSkn', 0);
let activityId = product && product.get('activityId', 0);
let cutRecordId = product && product.get('cutRecordId', 0);
if (!productSkn) {
return;
}
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.haggleDetail","params":{"type":"haggleDetail","title":"砍价详情", "productSkn":"${productSkn}", "activityId":"${activityId}","status": "${status}","cutRecordId":"${cutRecordId}"}}`;
if (cutRecordId == 0) {
url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.haggleDetail","params":{"type":"haggleDetail","title":"砍价详情", "productSkn":"${productSkn}", "activityId":"${activityId}","status": "${status}"}}`;
}
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onPressProduct(product) {
let {categoryType} = this.props.haggle;
let that = this;
if (categoryType == 1) {
this._jumpHaggleDetail(product, 0);
}else {
this.props.actions.getHaggleStatus(product.get('productSkn'), product.get('activityId'), function (json){
if (json) {
that.setState({showAlert:true, productItem:product});
}else {
that._jumpHaggleDetail(product, 1);
}
});
}
}
render() {
let {productList, myHaggleProductList, resourceInfo, isShowAlert, categoryType} = this.props.haggle;
let isFetching = productList.isFetching || myHaggleProductList.isFetching;
let isPullToRefresh = productList.isPullToRefresh || myHaggleProductList.isPullToRefresh;
return (
<View style={styles.container}>
<YHAlert
ref='YHAlert'
isShow = {this.state.showAlert}
title = '当前有未完成的砍价!'
content={'该商品正在砍价中,去邀请好友帮忙砍价吧!'}
>
<YHAlertItem key={'minCancel'} param={this.state.productItem} showStatus='LightGrayButton'
handleAction={(param) => {
{this._hiddenTipsAlertDialog()};
}}>取消</YHAlertItem>
<YHAlertItem key={'minSure'} param={this.state.productItem} showStatus='redButton'
handleAction={(param) => {
{this._hiddenTipsAlertDialog()};
{this._jumpHaggleDetail(param, 0)};
}}>查看详情</YHAlertItem>
</YHAlert>
<HaggleList
data= {categoryType==0 ? productList : myHaggleProductList}
resourceInfo = {resourceInfo}
onEndReached={this._onEndReached}
categoryType={categoryType}
onPressProduct={this._onPressProduct}
firstHaggle={this._firstHaggle}
continueHaggle={this._continueHaggle}
onPressCategory={this._onPressCategory}
onStop={this._timerStop}
resourceJumpWithUrl={this._resourceJumpWithUrl}
onRefresh={this._onRefresh}
/>
<LoadingIndicator isVisible={isFetching && !isPullToRefresh}/>
</View>
);
}
}
let {width} = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
export default connect(mapStateToProps, mapDispatchToProps)(HaggleListContainer);
... ...
'use strict';
import ReactNative from 'react-native';
const {
SET_PLATFORM,
SET_CHANNEL,
SET_HOST,
SET_SERVICE_HOST,
} = require('../../constants/actionTypes').default;
export function setPlatform(platform) {
return {
type: SET_PLATFORM,
payload: platform
};
}
export function setChannel(channel) {
return {
type: SET_CHANNEL,
payload: channel
};
}
export function setHost(host) {
return {
type: SET_HOST,
payload: host
};
}
export function setServiceHost(host) {
return {
type: SET_SERVICE_HOST,
payload: host
};
}
... ...
'use strict';
import {Record, List, Map} from 'immutable';
let InitialState = Record({
platform: 'ios', // ios, android
channel: 1, // 1 - boy, 2 - girl, 3 - kid, 4 - lifestyle, 5 - yoho
host: 'http://api.yoho.cn',
serviceHost: 'http://api.yoho.cn',
});
export default InitialState;
... ...
'use strict';
import InitialState from './appInitialState';
const {
SET_PLATFORM,
SET_CHANNEL,
SET_HOST,
SET_SERVICE_HOST,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function appReducer(state = initialState, action) {
if (!(state instanceof InitialState)) return initialState.merge(state);
switch (action.type) {
case SET_PLATFORM:
return state.set('platform', action.payload);
case SET_CHANNEL:
return state.set('channel', action.payload);
case SET_HOST:
return state.set('host', action.payload);
case SET_SERVICE_HOST:
return state.set('serviceHost', action.payload);
}
return state;
}
... ...
'use strict';
import ReactNative from 'react-native';
import haggleService from '../../services/haggleService';
const Platform = require('Platform');
const {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
SET_SELECTED_CATEGORY,
SHOW_HASCUT_ALERT,
HAGGLE_STATUS_SUCCESS,
REFRESH_PRODUCT_LIST,
MYHAGGLE_PRODUCT_LIST_REQUEST,
MYHAGGLE_PRODUCT_LIST_SUCCESS,
MYHAGGLE_PRODUCT_LIST_FAILURE,
RESOURCE_INFO_REQUEST,
RESOURCE_INFO_SUCCESS,
RESOURCE_INFO_FAILURE,
} = require('../../constants/actionTypes').default;
export function setSelectedCategoryType(index) {
return {
type: SET_SELECTED_CATEGORY,
payload: index
};
}
export function setSelectedCategory(index) {
return (dispatch, getState) => {
dispatch(setSelectedCategoryType(index));
let {haggle} = getState();
let productList = haggle.productList;
let myproductList = haggle.myHaggleProductList;
let list = productList.list ? productList.list.toArray() : [];
let myList = myproductList.list ? myproductList.list.toArray() : [];
if (index == 0 && list.length == 0) {
dispatch(getHaggleProductList());
}else if (index == 1 && myList.length == 0){
dispatch(getMyHaggleProductList());
}
};
}
export function refreshProductList(index) {
return (dispatch, getState) => {
dispatch({
type: REFRESH_PRODUCT_LIST,
payload: {index}
});
if (index == 0) {
dispatch(getHaggleProductList());
}else if (index == 1){
dispatch(getMyHaggleProductList());
}
};
}
export function showHasCutAlert() {
return {
type: SHOW_HASCUT_ALERT,
};
}
export function productListRequest() {
return {
type: PRODUCT_LIST_REQUEST,
};
}
export function productListSuccess(json) {
return {
type: PRODUCT_LIST_SUCCESS,
payload: json
};
}
export function haggleStatusSuccess(json) {
return {
type: HAGGLE_STATUS_SUCCESS,
payload: json
};
}
export function productListFailure(error) {
return {
type: PRODUCT_LIST_FAILURE,
payload: error
};
}
export function myHaggleProductListRequest() {
return {
type: MYHAGGLE_PRODUCT_LIST_REQUEST,
};
}
export function myHaggleProductListSuccess(json) {
return {
type: MYHAGGLE_PRODUCT_LIST_SUCCESS,
payload: json
};
}
export function myHaggleProductListFailure(error) {
return {
type: MYHAGGLE_PRODUCT_LIST_FAILURE,
payload: error
};
}
export function getResourceRequest() {
return {
type: RESOURCE_INFO_REQUEST,
};
}
export function getResourceSuccess(json) {
return {
type: RESOURCE_INFO_SUCCESS,
payload: json
};
}
export function getResourceFailure(error) {
return {
type: RESOURCE_INFO_FAILURE,
payload: error
};
}
export function getHaggleProductList() {
return (dispatch, getState) => {
let {app, haggle} = getState();
let list = haggle.productList;
/**
* page: 0, //当前页面
* page_size: 20, //每页显示的数量
* total: 0, //总共多少条
* page_total: 0, //总共多少页
* endReached: false, //到达底部
*/
if (list.isFetching || list.endReached || list.error || (!list.endReached && list.page_total === 1)) {
return;
}
let page = list.page + 1;
let pageSize = list.page_size;
dispatch(productListRequest());
return new haggleService(app.host).fetchHaggleProductList(page, pageSize)
.then(json => {
let payload = json;
payload.endReached = (payload.page === payload.page_total) && (payload.page_total !== 1);
if (payload.page > 1) {
let oldList = list.list.toJS();
let newList = [...oldList, ...payload.list];
payload.list = newList;
}
dispatch(productListSuccess(payload));
})
.catch(error => {
dispatch(productListFailure(error));
});
};
}
export function getMyHaggleProductList() {
return (dispatch, getState) => {
let {app, haggle} = getState();
let list = haggle.myHaggleProductList;
/**
* page: 0, //当前页面
* page_size: 20, //每页显示的数量
* total: 0, //总共多少条
* page_total: 0, //总共多少页
* endReached: false, //到达底部
*/
if (list.isFetching || list.endReached || list.error || (!list.endReached && list.page_total === 1)) {
return;
}
let page = list.page + 1;
let pageSize = list.page_size;
let fetchMyHaggleProductList = (uid) => {
dispatch(myHaggleProductListRequest());
return new haggleService(app.host).fetchMyHaggleProductList(uid, page, pageSize)
.then(json => {
let payload = json;
payload.endReached = (payload.page === payload.page_total) && (payload.page_total !== 1);
if (payload.page > 1) {
let oldList = list.list.toJS();
let newList = [...oldList, ...payload.list];
payload.list = newList;
}
dispatch(myHaggleProductListSuccess(payload));
})
.catch(error => {
dispatch(myHaggleProductListFailure(error));
});
};
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
fetchMyHaggleProductList(uid);
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
fetchMyHaggleProductList(uid);
})
.catch(error => {
});
});
};
}
export function getHaggleStatus(productSkn, activityId, callBack) {
return (dispatch, getState) => {
let {app} = getState();
let fetchHaggleStatus = (uid) => {
return new haggleService(app.host).fetcheProductHaggleStatus(uid, activityId, productSkn)
.then(json => {
let payload = json;
dispatch(haggleStatusSuccess(payload));
callBack && typeof callBack === 'function' && callBack(payload)
})
.catch(error => {
});
};
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
fetchHaggleStatus(uid)
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
fetchHaggleStatus(uid)
})
.catch(error => {
});
});
};
}
export function getResourceInfo(callback) {
return (dispatch, getState) => {
let {app} = getState();
dispatch(getResourceRequest());
return new haggleService(app.host).fetchResourceInfo('84c207985a995658669e019ce98171ed')
.then(json => {
dispatch(getResourceSuccess(json));
callback && typeof callback === 'function' && callback(json)
})
.catch(error => {
dispatch(getResourceFailure(error));
});
};
}
... ...
'use strict';
//http://apidoc.yohops.com/
import {List, Map, Record} from 'immutable';
let InitialState = Record({
productList: new (Record({
isFetching: false,
error: null,
page: 0, //当前页面
page_size: 20, //每页显示的数量
total: 0, //总共多少条
page_total: 0, //总共多少页
endReached: false, //到达底部
isPullToRefresh: false,
list: List(),
})),
myHaggleProductList: new (Record({
isFetching: false,
error: null,
page: 0, //当前页面
page_size: 20, //每页显示的数量
total: 0, //总共多少条
page_total: 0, //总共多少页
endReached: false, //到达底部
isPullToRefresh: false,
list: List(),
})),
resourceInfo: new (Record({
isFetching: false,
error: null,
resourceList: List(),
})),
isShowAlert: false,
categoryType: 0,
});
export default InitialState;
... ...
'use strict';
import InitialState from './haggleInitialState';
import Immutable, {List} from 'immutable';
const {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
MYHAGGLE_PRODUCT_LIST_REQUEST,
MYHAGGLE_PRODUCT_LIST_SUCCESS,
MYHAGGLE_PRODUCT_LIST_FAILURE,
RESOURCE_INFO_REQUEST,
RESOURCE_INFO_SUCCESS,
RESOURCE_INFO_FAILURE,
SET_SELECTED_CATEGORY,
SHOW_HASCUT_ALERT,
REFRESH_PRODUCT_LIST,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function couponReducer(state = initialState, action) {
if (!(state instanceof InitialState)) {
return initialState.merge(state);
}
switch (action.type) {
case PRODUCT_LIST_REQUEST: {
return state.setIn(['productList', 'isFetching'], true)
.setIn(['productList', 'error'], null);
}
case PRODUCT_LIST_SUCCESS: {
let {
page,
page_total,
endReached,
list,
} = action.payload;
return state.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'page'], page)
.setIn(['productList', 'page_total'], page_total)
.setIn(['productList', 'endReached'], endReached)
.setIn(['productList', 'list'], Immutable.fromJS(list))
.setIn(['productList', 'error'], null)
.setIn(['productList', 'isPullToRefresh'], false);
}
case PRODUCT_LIST_FAILURE: {
return state.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], action.payload)
.setIn(['productList', 'isPullToRefresh'], false);
}
case MYHAGGLE_PRODUCT_LIST_REQUEST: {
return state.setIn(['myHaggleProductList', 'isFetching'], true)
.setIn(['myHaggleProductList', 'error'], null);
}
case MYHAGGLE_PRODUCT_LIST_SUCCESS: {
let {
page,
page_total,
endReached,
list,
} = action.payload;
return state.setIn(['myHaggleProductList', 'isFetching'], false)
.setIn(['myHaggleProductList', 'page'], page)
.setIn(['myHaggleProductList', 'page_total'], page_total)
.setIn(['myHaggleProductList', 'endReached'], endReached)
.setIn(['myHaggleProductList', 'list'], Immutable.fromJS(list))
.setIn(['myHaggleProductList', 'error'], null)
.setIn(['myHaggleProductList', 'isPullToRefresh'], false);
}
case MYHAGGLE_PRODUCT_LIST_FAILURE: {
return state.setIn(['myHaggleProductList', 'isFetching'], false)
.setIn(['myHaggleProductList', 'error'], action.payload)
.setIn(['myHaggleProductList', 'isPullToRefresh'], false);
}
case SET_SELECTED_CATEGORY: {
return state.set('categoryType', action.payload);
}
case REFRESH_PRODUCT_LIST: {
let {
index,
} = action.payload;
if (index === 0) {
return state.setIn(['productList', 'page'], 0)
.setIn(['productList', 'page_total'], 0)
.setIn(['productList', 'endReached'], false)
.setIn(['productList', 'error'], null)
.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'isPullToRefresh'], true);
}else {
return state.setIn(['myHaggleProductList', 'page'], 0)
.setIn(['myHaggleProductList', 'page_total'], 0)
.setIn(['myHaggleProductList', 'endReached'], false)
.setIn(['myHaggleProductList', 'error'], null)
.setIn(['myHaggleProductList', 'isFetching'], false)
.setIn(['myHaggleProductList', 'isPullToRefresh'], true);
}
}
break;
case SHOW_HASCUT_ALERT: {
return state.set('isShowAlert', true);
}
case RESOURCE_INFO_REQUEST: {
return state.setIn(['resourceInfo', 'isFetching'], true)
.setIn(['resourceInfo', 'error'], action.payload);
}
case RESOURCE_INFO_SUCCESS: {
return state.setIn(['resourceInfo', 'isFetching'], false)
.setIn(['resourceInfo', 'resourceList'], Immutable.fromJS(action.payload))
.setIn(['resourceInfo', 'error'], null);
}
case RESOURCE_INFO_FAILURE: {
return state.setIn(['resourceInfo', 'isFetching'], false)
.setIn(['resourceInfo', 'error'], null);
}
}
return state;
}
... ...
import {combineReducers} from 'redux';
import haggle from './haggleListReducer/haggleReducer';
import app from './app/appReducer';
const rootReducer = combineReducers({
app,
haggle
});
export default rootReducer;
... ...
'use strict';
import Request from '../../common/services/NativeRequest';
import {Platform} from "react-native";
export default class haggleService {
constructor(host) {
let baseURL = 'http://api.yoho.cn';
if (host) {
baseURL = host;
}
this.api = new Request(baseURL);
}
//(砍价商品列表)
async fetchHaggleProductList(page, size) {
return await this.api.get({
url: '',
body: {
page,
limit:size,
method: 'app.search.cutprice.productList',
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
//(我的砍价商品列表)
async fetchMyHaggleProductList(uid, page, size) {
return await this.api.get({
url: '',
body: {
uid,
page,
limit:size,
method: 'app.cutdownprice.myCutPriceList',
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async fetcheProductHaggleStatus(uid, activityId, productSkn) {
return await this.api.get({
url: '',
body: {
uid,
productSkn,
activityId,
method: 'app.cutdownprice.checkdoing',
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async fetchResourceInfo(content_code) {
return await this.api.get({
url: '/operations/api/v5/resource/get',
body: {
content_code,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
}
... ...
/**
* # configureStore.js
*
* A Redux boilerplate setup
*
*/
'use strict';
/**
* ## Imports
*
* redux functions
*/
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {createLogger} from 'redux-logger';
/**
* ## Reducer
* The reducer contains the 4 reducers from
* device, global, auth, profile
*/
import reducer from '../reducers';
const logger = createLogger({
predicate: (getState, action) => process.env.NODE_ENV === `development`
});
/**
* ## creatStoreWithMiddleware
* Like the name...
*/
const createStoreWithMiddleware = applyMiddleware(
thunk,
logger
)(createStore);
/**
* ## configureStore
* @param {Object} the state with for keys:
* device, global, auth, profile
*
*/
export default function configureStore(initialState) {
return createStoreWithMiddleware(reducer, initialState);
};
... ...