Authored by 孙凯

add 列表 review by chenling

import React, { Component } from 'react';
import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View,ListView } from 'react-native';
import { connect } from 'react-redux';
import Immutable, { List } from 'immutable';
import Focus from './floor/Focus';
import ProductCell from './recommend/ProductCell';
const { width } = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
function mapStateToProps(store) {
const { shareDetail } = store;
return {
shareDetail
export default class ShareDetail extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderBottom = this._renderBottom.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
}
}
class ShareDetail extends Component {
render() {
_renderBottom(productInfo,isCollect) {
return (
<View style={styles.bottomBar}>
<View style={[styles.favContainer, styles.center]}>
<TouchableOpacity activeOpacity={0.8} onPress={() => this.props.addFavorite()} style={[styles.center]}>
<Image
style={styles.favorite}
resizeMode="contain"
source={isCollect === 'Y' ? require('../../recorder/images/brand/heart_ic_h.png') : require('../../recorder/images/brand/heart_ic_n.png')} />
<Text style={styles.favText}>收藏</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity activeOpacity={0.8} onPress={() => this.props.jumpWithUrl()}>
<View style={[styles.button, styles.center]}>
<Text style={styles.btnText}>查看商品详情</Text>
</View>
</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>
</View>
</TouchableOpacity>
</View>
</View>
);
}
_renderRow(rowData, sectionID, rowID) {
switch (sectionID) {
case 'productList': {
return (
<ProductCell
style={styles.listContainer}
key={'row' + rowID}
rowID={rowID}
data={rowData}
onPressProduct={this.props.onPressProduct}
/>
);
}
}
}
_renderHeader() {
let {shareDetail} = this.props;
let shareDetailInfo = shareDetail ? shareDetail.toJS(): {};
let productInfo = shareDetailInfo.productInfo;
let isCollect = shareDetail.isCollect;
if (!productInfo || productInfo == {}) {
return null
}
let goodsList = productInfo.goodsList;
let productPriceBo = productInfo.productPriceBo;
let goodsInfo = goodsList ? goodsList[0] : {};
if (!goodsInfo || !productPriceBo) {
return null
}
let goodsImagesList = goodsInfo ? goodsInfo.goodsImagesList: null;
let imgList = goodsImagesList && goodsImagesList.map(item => ({src: item.imageUrl}));
imgList = Immutable.fromJS(imgList);
return (
<View style={styles.container}>
<View style={[styles.navigatorBackground]}>
<View>
<View style={[styles.imgContainer]}>
<Focus
data={imgList}
height={480}
resourceJumpWithUrl={this.props.resourceJumpWithUrl}
/>
</View>
{/*<View style={styles.navigatorContainer}>
<View style={[styles.navigatorBtn, styles.center]} >
<TouchableOpacity onPress={() => this.props.goBack()}>
<Image source={require('../images/home_drawer_back.png')} />
</TouchableOpacity>
</View>
<View style={styles.navigatorBtnRightContainer}>
<View style={[styles.navigatorBtn,styles.navigatorBtnRight]}>
<TouchableOpacity onPress={() => null}>
<Image source={require('../images/yh_prd_more_icon.png')} />
</TouchableOpacity>
<View style={styles.infoContainer}>
<Text style={styles.name}>{productInfo.productName}</Text>
<View style={styles.priceContainer}>
<View style={styles.alignBottom}>
<Text style={styles.priceUnit}></Text>
<Text style={styles.price}>{productPriceBo.salesPrice}</Text>
<Text style={styles.origpriceUnit}></Text>
<Text style={styles.origPrice}>{productPriceBo.marketPrice}</Text>
</View>
</View>
</View>*/}
<ScrollView
style={styles.container}
showsVerticalScrollIndicator={false}>
<View style={[styles.imgContainer]}>
<Focus
data={imgList}
height={480}
resourceJumpWithUrl={this.props.resourceJumpWithUrl}
/>
</View>
<View style={styles.infoContainer}>
<Text style={styles.name}>{productInfo.productName}</Text>
<View style={styles.priceContainer}>
<View style={styles.alignBottom}>
<Text style={styles.priceUnit}></Text>
<Text style={styles.price}>{productPriceBo.salesPrice}</Text>
<Text style={styles.origpriceUnit}></Text>
<Text style={styles.origPrice}>{productPriceBo.marketPrice}</Text>
</View>
</View>
</View>
<View style={styles.divider} />
<View style={styles.promContainer}>
<Text style={styles.proDesc}>{productInfo.phrase}</Text>
</View>
<View style={styles.recommendContainer}>
<View style={styles.solid} />
<Text style={styles.recommendText}>查看其它推荐商品</Text>
<View style={styles.solid} />
</View>
</ScrollView>
<View style={styles.bottomBar}>
<View style={[styles.favContainer, styles.center]}>
<TouchableOpacity activeOpacity={0.8} onPress={() => this.props.addFavorite()} style={[styles.center]}>
<Image
style={styles.favorite}
resizeMode="contain"
source={isCollect === 'Y' ? require('../../recorder/images/brand/heart_ic_h.png') : require('../../recorder/images/brand/heart_ic_n.png')} />
<Text style={styles.favText}>收藏</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity activeOpacity={0.8} onPress={() => this.props.jumpWithUrl()}>
<View style={[styles.button, styles.center]}>
<Text style={styles.btnText}>查看商品详情</Text>
</View>
</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>
</View>
</TouchableOpacity>
</View>
</View>
<View style={styles.divider} />
<View style={styles.promContainer}>
<Text style={styles.proDesc}>{productInfo.phrase}</Text>
</View>
<View style={styles.recommendContainer}>
<View style={styles.solid} />
<Text style={styles.recommendText}>查看其它推荐商品</Text>
<View style={styles.solid} />
</View>
</View>
);
}
changeOpacity(e) {
let opacity = 1 - (500 - e.nativeEvent.contentOffset.y) / 500;
this.setState({opacity: opacity < 0.4 ? opacity : 0.4});
render() {
let {shareDetail} = this.props;
let shareDetailInfo = shareDetail ? shareDetail.toJS(): {};
let productInfo = shareDetailInfo.productInfo;
let isCollect = shareDetail.isCollect;
let productList = shareDetail.productList;
let product_List = productList.product_list ? productList.product_list.toArray() : [];
let dataSource = {
productList: product_List,
};
return (
<View style={styles.container}>
<ListView
contentContainerStyle={styles.contentContainer}
enableEmptySections={true}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
onEndReached={() => {
if (product_List.size !== 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
/>
{this._renderBottom(productInfo,isCollect)}
</View>
);
}
}
... ... @@ -123,33 +152,10 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
navigatorBackground: {
height: 44 * DEVICE_WIDTH_RATIO,
width,
position: 'absolute',
top: 0,
},
navigatorContainer: {
backgroundColor: 'transparent',
height: 44 * DEVICE_WIDTH_RATIO,
flexDirection: 'row',
alignItems: 'center',
},
navigatorBtn: {
backgroundColor: 'rgba(0, 0, 0, 0.4)',
height: 32 * DEVICE_WIDTH_RATIO,
width: 32 * DEVICE_WIDTH_RATIO,
overflow: 'hidden',
borderRadius: 16 * DEVICE_WIDTH_RATIO,
marginLeft: 9 * DEVICE_WIDTH_RATIO,
},
navigatorBtnRightContainer: {
flex: 1,
alignItems: 'flex-end',
},
navigatorBtnRight: {
marginLeft: 0,
marginRight: 9 * DEVICE_WIDTH_RATIO,
contentContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
backgroundColor: 'white'
},
center: {
justifyContent: 'center',
... ... @@ -168,7 +174,7 @@ const styles = StyleSheet.create({
borderColor: '#5d5d5d'
},
infoContainer: {
height: 122 * DEVICE_WIDTH_RATIO,
height: 97 * DEVICE_WIDTH_RATIO,
paddingTop: 15 * DEVICE_WIDTH_RATIO,
paddingHorizontal: 15 * DEVICE_WIDTH_RATIO,
},
... ... @@ -279,7 +285,8 @@ const styles = StyleSheet.create({
btnText: {
color: '#FFFFFF',
fontSize: 15,
}
},
listContainer: {
width: width,
},
})
export default connect(mapStateToProps)(ShareDetail);
... ...
... ... @@ -142,4 +142,8 @@ export default keyMirror({
SHARE_CODE_INFO_REQUEST: null,
SHARE_CODE_INFO_SUCCESS: null,
SHARE_CODE_INFO_FAILURE: null,
PRODUCT_LIST_REQUEST: null,
PRODUCT_LIST_SUCCESS: null,
PRODUCT_LIST_FAILURE: null,
});
... ...
import React, { Component } from 'react';
import ReactNative,{ StyleSheet, View, NativeModules ,Dimensions} from "react-native";
import ReactNative,{ StyleSheet, View, NativeModules ,Dimensions,Platform} from "react-native";
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Map } from 'immutable';
... ... @@ -38,6 +38,8 @@ class ShareDetailContainer extends Component {
this._goBack = this._goBack.bind(this);
this._addFavorite = this._addFavorite.bind(this);
this._jumpWithUrl = this._jumpWithUrl.bind(this);
this._onEndReached = this._onEndReached.bind(this);
this._onPressProduct = this._onPressProduct.bind(this);
}
render() {
... ... @@ -50,17 +52,42 @@ class ShareDetailContainer extends Component {
goBack={this._goBack}
addFavorite={this._addFavorite}
jumpWithUrl={this._jumpWithUrl}
onEndReached={this._onEndReached}
onPressProduct={this._onPressProduct}
/>
</View>
)
}
componentDidMount() {
this.props.actions.getProductList(false,this.props.product_skn);
this.props.actions.fetchShareCodeInfo(this.props.product_skn);
this.props.actions.fetchShareDetail({product_skn: this.props.product_skn});
this.props.actions.fetchFavoriteInfo({id: this.props.product_id, type: 'product'})
}
_onPressProduct(product) {
let productSkn = product && product.get('product_skn', 0);
let product_id = product && product.get('product_id', 0);
if (!productSkn) {
return;
}
let pageName = 'iFP_Alliance';
if (Platform.OS === 'android') {
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}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onEndReached() {
this.props.actions.getProductList(false,this.props.product_skn);
}
_addFavorite = () => {
this.props.actions.addFavorite({id: this.props.product_id, type: 'product'});
}
... ...
... ... @@ -2,6 +2,7 @@
import ReactNative from 'react-native';
import Service from '../../services/ShareDetailService';
const Platform = require('Platform');
const {
SHARE_DETAIL_REQUEST,
... ... @@ -16,6 +17,11 @@ const {
SHARE_CODE_INFO_REQUEST,
SHARE_CODE_INFO_SUCCESS,
SHARE_CODE_INFO_FAILURE,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
} = require('../../constants/actionTypes').default;
export function fetchShareCodeInfoRequest() {
... ... @@ -172,3 +178,56 @@ export function fetchFavoriteInfo(params) {
dispatch(addFavoriteSuccess(data ? 'Y' : 'N'));
}
}
export function productListRequest() {
return {
type: PRODUCT_LIST_REQUEST,
};
}
export function productListSuccess(json) {
return {
type: PRODUCT_LIST_SUCCESS,
payload: json
}
}
export function productListFailure(error) {
return {
type: PRODUCT_LIST_FAILURE,
payload: error
}
}
export function getProductList(reload=false,product_skn) {
return (dispatch, getState) => {
let {app, shareDetail} = getState();
let {productList} = shareDetail;
if (reload) {
} else {
if (productList.isFetching || productList.endReached || productList.error) {
return;
}
}
let page = productList.page + 1;
let size = productList.pageSize;
let fromPage = Platform.OS === 'android' ? 'aFP_AllianceProductDetail' : 'iFP_AllianceProductDetail';
dispatch(productListRequest());
return new Service(app.host).fetchProductList(page, size,fromPage,product_skn)
.then(json => {
json.endReached = json.page == json.page_total;
if (json.page > 1) {
let oldList = productList.product_list.toJS();
let product_list = [...oldList, ...json.product_list];
json.product_list = product_list;
}
dispatch(productListSuccess(json));
})
.catch(error => {
dispatch(productListFailure(error));
});
};
}
... ...
... ... @@ -8,6 +8,17 @@ let InitialState = Record({
productInfo: {},
isCollect: 'N',
shareCodeInfo: null,
productList: new (Record({
isFetching: false,
error: null,
product_list: List(),
page: 0,
total: 0,
pagetotal: 0,
pageSize: 20,//60,
endReached: false,
})),
});
export default InitialState;
... ...
... ... @@ -17,6 +17,11 @@ const {
SHARE_CODE_INFO_SUCCESS,
SHARE_CODE_INFO_FAILURE,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
... ... @@ -55,6 +60,31 @@ export default function appReducer(state = initialState, action) {
case SHARE_CODE_INFO_FAILURE:
return state.set('isFetching', false)
.set('error', action.payload);
case PRODUCT_LIST_REQUEST: {
return state.setIn(['productList', 'isFetching'], true)
.setIn(['productList', 'error'], null);
}
case PRODUCT_LIST_SUCCESS: {
let {
product_list,
page,
page_total,
total,
endReached,
} = action.payload;
return state.setIn(['productList', 'page'], page)
.setIn(['productList', 'pagetotal'], page_total)
.setIn(['productList', 'total'], total)
.setIn(['productList', 'product_list'], Immutable.fromJS(product_list))
.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], null)
.setIn(['productList', 'endReached'], endReached);
}
case PRODUCT_LIST_FAILURE: {
return state.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], action.payload);
}
}
return state;
... ...
... ... @@ -9,6 +9,24 @@ export default class Service {
this.api = new Request(baseURL);
}
async fetchProductList(page=1, size=20,fromPage,product_skn) {
return await this.api.get({
url: '',
body: {
method: 'app.search.cpsSimilar.productList',
page,
size,
product_skn,
fromPage,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async fetchShareCodeInfo(skn,unionType) {
return await this.api.get({
... ...