Authored by 孙凯

add 列表 review by chenling

1 import React, { Component } from 'react'; 1 import React, { Component } from 'react';
2 -import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; 2 +import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View,ListView } from 'react-native';
3 import { connect } from 'react-redux'; 3 import { connect } from 'react-redux';
4 import Immutable, { List } from 'immutable'; 4 import Immutable, { List } from 'immutable';
5 5
6 import Focus from './floor/Focus'; 6 import Focus from './floor/Focus';
  7 +import ProductCell from './recommend/ProductCell';
7 8
8 const { width } = Dimensions.get('window'); 9 const { width } = Dimensions.get('window');
9 const DEVICE_WIDTH_RATIO = width / 375; 10 const DEVICE_WIDTH_RATIO = width / 375;
10 11
11 -function mapStateToProps(store) {  
12 - const { shareDetail } = store;  
13 - return {  
14 - shareDetail 12 +export default class ShareDetail extends Component {
  13 +
  14 + constructor(props) {
  15 + super(props);
  16 + this._renderRow = this._renderRow.bind(this);
  17 + this._renderHeader = this._renderHeader.bind(this);
  18 + this._renderBottom = this._renderBottom.bind(this);
  19 +
  20 + this.dataSource = new ListView.DataSource({
  21 + rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
  22 + sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
  23 + });
15 } 24 }
16 -}  
17 25
18 -class ShareDetail extends Component {  
19 - render() { 26 + _renderBottom(productInfo,isCollect) {
  27 + return (
  28 + <View style={styles.bottomBar}>
  29 + <View style={[styles.favContainer, styles.center]}>
  30 + <TouchableOpacity activeOpacity={0.8} onPress={() => this.props.addFavorite()} style={[styles.center]}>
  31 + <Image
  32 + style={styles.favorite}
  33 + resizeMode="contain"
  34 + source={isCollect === 'Y' ? require('../../recorder/images/brand/heart_ic_h.png') : require('../../recorder/images/brand/heart_ic_n.png')} />
  35 + <Text style={styles.favText}>收藏</Text>
  36 + </TouchableOpacity>
  37 + </View>
  38 + <View style={styles.buttonContainer}>
  39 + <TouchableOpacity activeOpacity={0.8} onPress={() => this.props.jumpWithUrl()}>
  40 + <View style={[styles.button, styles.center]}>
  41 + <Text style={styles.btnText}>查看商品详情</Text>
  42 + </View>
  43 + </TouchableOpacity>
  44 + <TouchableOpacity activeOpacity={0.8} onPress={() => this.props.showShareView(productInfo)}>
  45 + <View style={[styles.button, styles.center, styles.red]}>
  46 + <Text style={styles.btnText}>{'分享可赚¥' + productInfo.rebatesAmount}</Text>
  47 + </View>
  48 + </TouchableOpacity>
  49 + </View>
  50 + </View>
  51 + );
  52 + }
  53 + _renderRow(rowData, sectionID, rowID) {
  54 + switch (sectionID) {
  55 + case 'productList': {
  56 + return (
  57 + <ProductCell
  58 + style={styles.listContainer}
  59 + key={'row' + rowID}
  60 + rowID={rowID}
  61 + data={rowData}
  62 + onPressProduct={this.props.onPressProduct}
  63 + />
  64 + );
  65 + }
  66 + }
  67 + }
  68 +
  69 + _renderHeader() {
  70 +
20 let {shareDetail} = this.props; 71 let {shareDetail} = this.props;
21 let shareDetailInfo = shareDetail ? shareDetail.toJS(): {}; 72 let shareDetailInfo = shareDetail ? shareDetail.toJS(): {};
22 let productInfo = shareDetailInfo.productInfo; 73 let productInfo = shareDetailInfo.productInfo;
23 -  
24 - let isCollect = shareDetail.isCollect;  
25 if (!productInfo || productInfo == {}) { 74 if (!productInfo || productInfo == {}) {
26 return null 75 return null
27 } 76 }
28 let goodsList = productInfo.goodsList; 77 let goodsList = productInfo.goodsList;
29 let productPriceBo = productInfo.productPriceBo; 78 let productPriceBo = productInfo.productPriceBo;
30 let goodsInfo = goodsList ? goodsList[0] : {}; 79 let goodsInfo = goodsList ? goodsList[0] : {};
31 -  
32 if (!goodsInfo || !productPriceBo) { 80 if (!goodsInfo || !productPriceBo) {
33 return null 81 return null
34 } 82 }
35 -  
36 let goodsImagesList = goodsInfo ? goodsInfo.goodsImagesList: null; 83 let goodsImagesList = goodsInfo ? goodsInfo.goodsImagesList: null;
37 let imgList = goodsImagesList && goodsImagesList.map(item => ({src: item.imageUrl})); 84 let imgList = goodsImagesList && goodsImagesList.map(item => ({src: item.imageUrl}));
38 imgList = Immutable.fromJS(imgList); 85 imgList = Immutable.fromJS(imgList);
39 86
40 return ( 87 return (
41 - <View style={styles.container}>  
42 - <View style={[styles.navigatorBackground]}>  
43 - </View>  
44 - {/*<View style={styles.navigatorContainer}>  
45 - <View style={[styles.navigatorBtn, styles.center]} >  
46 - <TouchableOpacity onPress={() => this.props.goBack()}>  
47 - <Image source={require('../images/home_drawer_back.png')} />  
48 - </TouchableOpacity>  
49 - </View>  
50 - <View style={styles.navigatorBtnRightContainer}>  
51 - <View style={[styles.navigatorBtn,styles.navigatorBtnRight]}>  
52 - <TouchableOpacity onPress={() => null}>  
53 - <Image source={require('../images/yh_prd_more_icon.png')} />  
54 - </TouchableOpacity>  
55 - </View>  
56 - </View>  
57 - </View>*/}  
58 - <ScrollView  
59 - style={styles.container}  
60 - showsVerticalScrollIndicator={false}> 88 + <View>
61 <View style={[styles.imgContainer]}> 89 <View style={[styles.imgContainer]}>
62 <Focus 90 <Focus
63 data={imgList} 91 data={imgList}
@@ -85,37 +113,38 @@ class ShareDetail extends Component { @@ -85,37 +113,38 @@ class ShareDetail extends Component {
85 <Text style={styles.recommendText}>查看其它推荐商品</Text> 113 <Text style={styles.recommendText}>查看其它推荐商品</Text>
86 <View style={styles.solid} /> 114 <View style={styles.solid} />
87 </View> 115 </View>
88 - </ScrollView>  
89 - <View style={styles.bottomBar}>  
90 - <View style={[styles.favContainer, styles.center]}>  
91 - <TouchableOpacity activeOpacity={0.8} onPress={() => this.props.addFavorite()} style={[styles.center]}>  
92 - <Image  
93 - style={styles.favorite}  
94 - resizeMode="contain"  
95 - source={isCollect === 'Y' ? require('../../recorder/images/brand/heart_ic_h.png') : require('../../recorder/images/brand/heart_ic_n.png')} />  
96 - <Text style={styles.favText}>收藏</Text>  
97 - </TouchableOpacity>  
98 - </View>  
99 - <View style={styles.buttonContainer}>  
100 - <TouchableOpacity activeOpacity={0.8} onPress={() => this.props.jumpWithUrl()}>  
101 - <View style={[styles.button, styles.center]}>  
102 - <Text style={styles.btnText}>查看商品详情</Text>  
103 - </View>  
104 - </TouchableOpacity>  
105 - <TouchableOpacity activeOpacity={0.8} onPress={() => this.props.showShareView(productInfo)}>  
106 - <View style={[styles.button, styles.center, styles.red]}>  
107 - <Text style={styles.btnText}>{'分享可赚¥' + productInfo.rebatesAmount}</Text>  
108 - </View>  
109 - </TouchableOpacity>  
110 - </View>  
111 - </View>  
112 </View> 116 </View>
113 ); 117 );
114 } 118 }
115 119
116 - changeOpacity(e) {  
117 - let opacity = 1 - (500 - e.nativeEvent.contentOffset.y) / 500;  
118 - this.setState({opacity: opacity < 0.4 ? opacity : 0.4}); 120 + render() {
  121 + let {shareDetail} = this.props;
  122 + let shareDetailInfo = shareDetail ? shareDetail.toJS(): {};
  123 + let productInfo = shareDetailInfo.productInfo;
  124 + let isCollect = shareDetail.isCollect;
  125 + let productList = shareDetail.productList;
  126 + let product_List = productList.product_list ? productList.product_list.toArray() : [];
  127 + let dataSource = {
  128 + productList: product_List,
  129 + };
  130 +
  131 + return (
  132 + <View style={styles.container}>
  133 + <ListView
  134 + contentContainerStyle={styles.contentContainer}
  135 + enableEmptySections={true}
  136 + dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
  137 + renderRow={this._renderRow}
  138 + renderHeader={this._renderHeader}
  139 + onEndReached={() => {
  140 + if (product_List.size !== 0) {
  141 + this.props.onEndReached && this.props.onEndReached();
  142 + }
  143 + }}
  144 + />
  145 + {this._renderBottom(productInfo,isCollect)}
  146 + </View>
  147 + );
119 } 148 }
120 } 149 }
121 150
@@ -123,33 +152,10 @@ const styles = StyleSheet.create({ @@ -123,33 +152,10 @@ const styles = StyleSheet.create({
123 container: { 152 container: {
124 flex: 1, 153 flex: 1,
125 }, 154 },
126 - navigatorBackground: {  
127 - height: 44 * DEVICE_WIDTH_RATIO,  
128 - width,  
129 - position: 'absolute',  
130 - top: 0,  
131 - },  
132 - navigatorContainer: {  
133 - backgroundColor: 'transparent',  
134 - height: 44 * DEVICE_WIDTH_RATIO, 155 + contentContainer: {
135 flexDirection: 'row', 156 flexDirection: 'row',
136 - alignItems: 'center',  
137 - },  
138 - navigatorBtn: {  
139 - backgroundColor: 'rgba(0, 0, 0, 0.4)',  
140 - height: 32 * DEVICE_WIDTH_RATIO,  
141 - width: 32 * DEVICE_WIDTH_RATIO,  
142 - overflow: 'hidden',  
143 - borderRadius: 16 * DEVICE_WIDTH_RATIO,  
144 - marginLeft: 9 * DEVICE_WIDTH_RATIO,  
145 - },  
146 - navigatorBtnRightContainer: {  
147 - flex: 1,  
148 - alignItems: 'flex-end',  
149 - },  
150 - navigatorBtnRight: {  
151 - marginLeft: 0,  
152 - marginRight: 9 * DEVICE_WIDTH_RATIO, 157 + flexWrap: 'wrap',
  158 + backgroundColor: 'white'
153 }, 159 },
154 center: { 160 center: {
155 justifyContent: 'center', 161 justifyContent: 'center',
@@ -168,7 +174,7 @@ const styles = StyleSheet.create({ @@ -168,7 +174,7 @@ const styles = StyleSheet.create({
168 borderColor: '#5d5d5d' 174 borderColor: '#5d5d5d'
169 }, 175 },
170 infoContainer: { 176 infoContainer: {
171 - height: 122 * DEVICE_WIDTH_RATIO, 177 + height: 97 * DEVICE_WIDTH_RATIO,
172 paddingTop: 15 * DEVICE_WIDTH_RATIO, 178 paddingTop: 15 * DEVICE_WIDTH_RATIO,
173 paddingHorizontal: 15 * DEVICE_WIDTH_RATIO, 179 paddingHorizontal: 15 * DEVICE_WIDTH_RATIO,
174 }, 180 },
@@ -279,7 +285,8 @@ const styles = StyleSheet.create({ @@ -279,7 +285,8 @@ const styles = StyleSheet.create({
279 btnText: { 285 btnText: {
280 color: '#FFFFFF', 286 color: '#FFFFFF',
281 fontSize: 15, 287 fontSize: 15,
282 - } 288 + },
  289 + listContainer: {
  290 + width: width,
  291 + },
283 }) 292 })
284 -  
285 -export default connect(mapStateToProps)(ShareDetail);  
@@ -142,4 +142,8 @@ export default keyMirror({ @@ -142,4 +142,8 @@ export default keyMirror({
142 SHARE_CODE_INFO_REQUEST: null, 142 SHARE_CODE_INFO_REQUEST: null,
143 SHARE_CODE_INFO_SUCCESS: null, 143 SHARE_CODE_INFO_SUCCESS: null,
144 SHARE_CODE_INFO_FAILURE: null, 144 SHARE_CODE_INFO_FAILURE: null,
  145 +
  146 + PRODUCT_LIST_REQUEST: null,
  147 + PRODUCT_LIST_SUCCESS: null,
  148 + PRODUCT_LIST_FAILURE: null,
145 }); 149 });
1 import React, { Component } from 'react'; 1 import React, { Component } from 'react';
2 -import ReactNative,{ StyleSheet, View, NativeModules ,Dimensions} from "react-native"; 2 +import ReactNative,{ StyleSheet, View, NativeModules ,Dimensions,Platform} from "react-native";
3 import { bindActionCreators } from 'redux'; 3 import { bindActionCreators } from 'redux';
4 import { connect } from 'react-redux'; 4 import { connect } from 'react-redux';
5 import { Map } from 'immutable'; 5 import { Map } from 'immutable';
@@ -38,6 +38,8 @@ class ShareDetailContainer extends Component { @@ -38,6 +38,8 @@ class ShareDetailContainer extends Component {
38 this._goBack = this._goBack.bind(this); 38 this._goBack = this._goBack.bind(this);
39 this._addFavorite = this._addFavorite.bind(this); 39 this._addFavorite = this._addFavorite.bind(this);
40 this._jumpWithUrl = this._jumpWithUrl.bind(this); 40 this._jumpWithUrl = this._jumpWithUrl.bind(this);
  41 + this._onEndReached = this._onEndReached.bind(this);
  42 + this._onPressProduct = this._onPressProduct.bind(this);
41 } 43 }
42 44
43 render() { 45 render() {
@@ -50,17 +52,42 @@ class ShareDetailContainer extends Component { @@ -50,17 +52,42 @@ class ShareDetailContainer extends Component {
50 goBack={this._goBack} 52 goBack={this._goBack}
51 addFavorite={this._addFavorite} 53 addFavorite={this._addFavorite}
52 jumpWithUrl={this._jumpWithUrl} 54 jumpWithUrl={this._jumpWithUrl}
  55 + onEndReached={this._onEndReached}
  56 + onPressProduct={this._onPressProduct}
53 /> 57 />
54 </View> 58 </View>
55 ) 59 )
56 } 60 }
57 61
58 componentDidMount() { 62 componentDidMount() {
  63 + this.props.actions.getProductList(false,this.props.product_skn);
59 this.props.actions.fetchShareCodeInfo(this.props.product_skn); 64 this.props.actions.fetchShareCodeInfo(this.props.product_skn);
60 this.props.actions.fetchShareDetail({product_skn: this.props.product_skn}); 65 this.props.actions.fetchShareDetail({product_skn: this.props.product_skn});
61 this.props.actions.fetchFavoriteInfo({id: this.props.product_id, type: 'product'}) 66 this.props.actions.fetchFavoriteInfo({id: this.props.product_id, type: 'product'})
62 } 67 }
63 68
  69 + _onPressProduct(product) {
  70 +
  71 + let productSkn = product && product.get('product_skn', 0);
  72 + let product_id = product && product.get('product_id', 0);
  73 + if (!productSkn) {
  74 + return;
  75 + }
  76 +
  77 + let pageName = 'iFP_Alliance';
  78 + if (Platform.OS === 'android') {
  79 + pageName = 'aFP_Alliance';
  80 + }
  81 +
  82 + 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}"}}`;
  83 +
  84 + ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
  85 + }
  86 +
  87 + _onEndReached() {
  88 + this.props.actions.getProductList(false,this.props.product_skn);
  89 + }
  90 +
64 _addFavorite = () => { 91 _addFavorite = () => {
65 this.props.actions.addFavorite({id: this.props.product_id, type: 'product'}); 92 this.props.actions.addFavorite({id: this.props.product_id, type: 'product'});
66 } 93 }
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 import ReactNative from 'react-native'; 3 import ReactNative from 'react-native';
4 import Service from '../../services/ShareDetailService'; 4 import Service from '../../services/ShareDetailService';
  5 +const Platform = require('Platform');
5 6
6 const { 7 const {
7 SHARE_DETAIL_REQUEST, 8 SHARE_DETAIL_REQUEST,
@@ -16,6 +17,11 @@ const { @@ -16,6 +17,11 @@ const {
16 SHARE_CODE_INFO_REQUEST, 17 SHARE_CODE_INFO_REQUEST,
17 SHARE_CODE_INFO_SUCCESS, 18 SHARE_CODE_INFO_SUCCESS,
18 SHARE_CODE_INFO_FAILURE, 19 SHARE_CODE_INFO_FAILURE,
  20 +
  21 + PRODUCT_LIST_REQUEST,
  22 + PRODUCT_LIST_SUCCESS,
  23 + PRODUCT_LIST_FAILURE,
  24 +
19 } = require('../../constants/actionTypes').default; 25 } = require('../../constants/actionTypes').default;
20 26
21 export function fetchShareCodeInfoRequest() { 27 export function fetchShareCodeInfoRequest() {
@@ -172,3 +178,56 @@ export function fetchFavoriteInfo(params) { @@ -172,3 +178,56 @@ export function fetchFavoriteInfo(params) {
172 dispatch(addFavoriteSuccess(data ? 'Y' : 'N')); 178 dispatch(addFavoriteSuccess(data ? 'Y' : 'N'));
173 } 179 }
174 } 180 }
  181 +
  182 +export function productListRequest() {
  183 + return {
  184 + type: PRODUCT_LIST_REQUEST,
  185 + };
  186 +}
  187 +
  188 +export function productListSuccess(json) {
  189 + return {
  190 + type: PRODUCT_LIST_SUCCESS,
  191 + payload: json
  192 + }
  193 +}
  194 +
  195 +export function productListFailure(error) {
  196 + return {
  197 + type: PRODUCT_LIST_FAILURE,
  198 + payload: error
  199 + }
  200 +}
  201 +
  202 +export function getProductList(reload=false,product_skn) {
  203 + return (dispatch, getState) => {
  204 + let {app, shareDetail} = getState();
  205 + let {productList} = shareDetail;
  206 + if (reload) {
  207 +
  208 + } else {
  209 + if (productList.isFetching || productList.endReached || productList.error) {
  210 + return;
  211 + }
  212 + }
  213 +
  214 + let page = productList.page + 1;
  215 + let size = productList.pageSize;
  216 + let fromPage = Platform.OS === 'android' ? 'aFP_AllianceProductDetail' : 'iFP_AllianceProductDetail';
  217 +
  218 + dispatch(productListRequest());
  219 + return new Service(app.host).fetchProductList(page, size,fromPage,product_skn)
  220 + .then(json => {
  221 + json.endReached = json.page == json.page_total;
  222 + if (json.page > 1) {
  223 + let oldList = productList.product_list.toJS();
  224 + let product_list = [...oldList, ...json.product_list];
  225 + json.product_list = product_list;
  226 + }
  227 + dispatch(productListSuccess(json));
  228 + })
  229 + .catch(error => {
  230 + dispatch(productListFailure(error));
  231 + });
  232 + };
  233 +}
@@ -8,6 +8,17 @@ let InitialState = Record({ @@ -8,6 +8,17 @@ let InitialState = Record({
8 productInfo: {}, 8 productInfo: {},
9 isCollect: 'N', 9 isCollect: 'N',
10 shareCodeInfo: null, 10 shareCodeInfo: null,
  11 +
  12 + productList: new (Record({
  13 + isFetching: false,
  14 + error: null,
  15 + product_list: List(),
  16 + page: 0,
  17 + total: 0,
  18 + pagetotal: 0,
  19 + pageSize: 20,//60,
  20 + endReached: false,
  21 + })),
11 }); 22 });
12 23
13 export default InitialState; 24 export default InitialState;
@@ -17,6 +17,11 @@ const { @@ -17,6 +17,11 @@ const {
17 SHARE_CODE_INFO_SUCCESS, 17 SHARE_CODE_INFO_SUCCESS,
18 SHARE_CODE_INFO_FAILURE, 18 SHARE_CODE_INFO_FAILURE,
19 19
  20 +
  21 + PRODUCT_LIST_REQUEST,
  22 + PRODUCT_LIST_SUCCESS,
  23 + PRODUCT_LIST_FAILURE,
  24 +
20 } = require('../../constants/actionTypes').default; 25 } = require('../../constants/actionTypes').default;
21 26
22 const initialState = new InitialState; 27 const initialState = new InitialState;
@@ -55,6 +60,31 @@ export default function appReducer(state = initialState, action) { @@ -55,6 +60,31 @@ export default function appReducer(state = initialState, action) {
55 case SHARE_CODE_INFO_FAILURE: 60 case SHARE_CODE_INFO_FAILURE:
56 return state.set('isFetching', false) 61 return state.set('isFetching', false)
57 .set('error', action.payload); 62 .set('error', action.payload);
  63 + case PRODUCT_LIST_REQUEST: {
  64 + return state.setIn(['productList', 'isFetching'], true)
  65 + .setIn(['productList', 'error'], null);
  66 + }
  67 + case PRODUCT_LIST_SUCCESS: {
  68 + let {
  69 + product_list,
  70 + page,
  71 + page_total,
  72 + total,
  73 + endReached,
  74 + } = action.payload;
  75 +
  76 + return state.setIn(['productList', 'page'], page)
  77 + .setIn(['productList', 'pagetotal'], page_total)
  78 + .setIn(['productList', 'total'], total)
  79 + .setIn(['productList', 'product_list'], Immutable.fromJS(product_list))
  80 + .setIn(['productList', 'isFetching'], false)
  81 + .setIn(['productList', 'error'], null)
  82 + .setIn(['productList', 'endReached'], endReached);
  83 + }
  84 + case PRODUCT_LIST_FAILURE: {
  85 + return state.setIn(['productList', 'isFetching'], false)
  86 + .setIn(['productList', 'error'], action.payload);
  87 + }
58 } 88 }
59 89
60 return state; 90 return state;
@@ -9,6 +9,24 @@ export default class Service { @@ -9,6 +9,24 @@ export default class Service {
9 this.api = new Request(baseURL); 9 this.api = new Request(baseURL);
10 } 10 }
11 11
  12 + async fetchProductList(page=1, size=20,fromPage,product_skn) {
  13 + return await this.api.get({
  14 + url: '',
  15 + body: {
  16 + method: 'app.search.cpsSimilar.productList',
  17 + page,
  18 + size,
  19 + product_skn,
  20 + fromPage,
  21 + }
  22 + })
  23 + .then((json) => {
  24 + return json;
  25 + })
  26 + .catch((error) => {
  27 + throw(error);
  28 + });
  29 + }
12 30
13 async fetchShareCodeInfo(skn,unionType) { 31 async fetchShareCodeInfo(skn,unionType) {
14 return await this.api.get({ 32 return await this.api.get({