Authored by yoho-js001

Part of good goods recommend. reviewed by redding.

'use strict';
import React, {Component} from 'react';
import Immutable, {Map} from 'immutable';
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
InteractionManager,
Platform,
} from 'react-native';
export default class Detail extends Component {
constructor(props) {
super(props);
this.state = {
totalHeight: 0
}
}
onLayout (reminder) {
let totalHeight = reminder.nativeEvent.layout.height + reminder.nativeEvent.layout.y + 15;
this.setState({totalHeight});
}
render() {
let data = this.props.data;
if (!data) {
return null;
}
return (
<View style={[styles.container, {height: this.state.totalHeight}]}>
<Text style={styles.title}>{data.get('product_name')} </Text>
<View style={styles.pricePannel}>
<Text style={styles.price}> {data.get('format_sales_price')}</Text>
<Text style={styles.originPrice}>{data.get('format_market_price')}</Text>
</View>
<Text style={styles.phrase}> {data.get('phrase')}</Text>
<View style={styles.tagPannel} onLayout={this.onLayout.bind(this)} >
<Text style={styles.tag}> {data.get('brand_info').get('brand_domain')}</Text>
<Text style={styles.tag}>{data.get('middle_sort_name')} </Text>
</View>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
width: width,
flexDirection: 'column',
},
title: {
fontSize: 16,
width: width - 30,
marginLeft: 15,
marginTop: 16,
marginBottom: 0,
color: '#444444',
// backgroundColor: 'red',
},
pricePannel: {
marginLeft: 15,
marginTop: 16,
width: width - 30,
flexDirection: 'row',
// backgroundColor: 'red',
},
price: {
fontSize: 16,
color: '#d0021b',
marginRight: 0,
},
originPrice: {
fontSize: 16,
color: '#b0b0b0',
marginLeft: 20,
textDecorationLine: 'line-through'
},
phrase: {
marginLeft: 15,
marginTop: 6,
width: width - 30,
fontSize: 13,
color: '#b0b0b0',
lineHeight: 20,
// backgroundColor: 'red',
},
tagPannel: {
marginLeft: 15,
marginTop: 15,
width: width - 30,
flexDirection: 'row',
marginBottom: 15,
// backgroundColor: 'red',
},
tag: {
paddingTop: 4,
paddingLeft: 10,
paddingRight: 10,
paddingBottom: 4,
marginLeft: 0,
marginRight: 10,
backgroundColor: '#f0f0f0',
fontSize: 12,
color: '#b0b0b0',
}
});
... ...
... ... @@ -3,6 +3,9 @@
import React, {Component} from 'react';
import Immutable, {Map} from 'immutable';
import LoadingIndicator from '../../../common/components/LoadingIndicator';
import GoodGoodsImagesView from './GoodGoodsImagesView'
import ContentCell from './ContentCell'
import BrandProductListCell from '../../../common/components/ListCell/ProductListCell';
import ReactNative, {
View,
... ... @@ -29,33 +32,111 @@ export default class Detail extends Component {
});
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
renderRow(rowData,sectionID,rowID,highlightRow) {
switch (sectionID) {
case 'images': {
let ary = [];
for (var i = 0; i < rowData.length; i++) {
let item = rowData[i];
let url = item.get('image_url');
let tempAry = url.split('?');
// url = url.replace('{width}', 420).replace('{height}', 562).replace('{mode}',2);
ary.push(tempAry[0]);
}
return (
<View style={{
width,
height: 300,
}}>
<GoodGoodsImagesView
style={{
width,
height: 300,
backgroundColor: '#f0f0f0'
}}
items={ary}
/>
<View style={styles.separator}/>
</View>
)
}
break;
case 'content': {
return <ContentCell data={rowData}/>
}
break;
case 'similarTitle': {
return(
<View style={{width: width, height: 40, borderColor: '#ededed', borderBottomWidth: 1, backgroundColor: '#f0f0f0'}}>
<Text style={{flex: 1, textAlign: 'center', color:'#444444', fontSize: 16, paddingTop: 12}}>猜你喜欢</Text>
</View>
)
}
break;
case 'similar': {
let paddingLeft = rowID % 2 == 1 ? rowMarginHorizontal / 2 : rowMarginHorizontal;
let customStyle = rowID == 0 || rowID == 1 ? {paddingLeft} : {paddingLeft};
return (
<BrandProductListCell
style={[styles.listContainer, customStyle]}
key={'row' + rowID}
rowID={rowID}
data={rowData}
onPressProduct={this.props.onPressProduct}
/>
);
}
break;
default:
}
return null;
}
render() {
let {product_skn, product, similarList} = this.props.resource;
let isFetching = product.get('isFetching')||similarList.get('isFetching');
let images = product.getIn(['data','goods_list',0,'images_list']);
let similar = similarList&&similarList.get('data')&&similarList.get('data').toArray().length?similarList.get('data').toArray():[];
let dataSource = {
'images': images&&images.toArray().length?[images.toArray()]:[],
'content':product&&product.get('data')?[product.get('data')]:[],
'similarTitle':similar.length?[1]:[],
'similar':similar,
};
//Test
product_skn = '51148345';
//
let isFetching = true;
let dataSource = {};
return (
<View style={styles.container}>
{!isFetching?<ListView
{!isFetching?
<View style={styles.container}>
<ListView
contentContainerStyle={styles.contentContainer}
enableEmptySections={true}
showsVerticalScrollIndicator={false}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this.renderRow}
/>:<LoadingIndicator
/>
<View style={styles.footer}>
<TouchableOpacity style={styles.favoriteButton}>
<View style={{flex: 1, flexDirection: 'row', alignItems: 'center'}}>
<Image style={styles.favIcon} source={require('../../image/love_tab_ic.png')}/>
<Text style={styles.favText}>已收藏</Text>
</View>
</TouchableOpacity>
<View style={styles.detailButton}>
<Text
style={styles.detailButtonText}
onPress={() => {this.props.onPressProduct&& this.props.onPressProduct(Immutable.fromJS({product_skn}))}}
>查看详情</Text>
</View>
</View>
</View>
:<LoadingIndicator
isVisible={isFetching}
/>}
</View>
... ... @@ -65,16 +146,64 @@ export default class Detail extends Component {
let {width, height} = Dimensions.get('window');
let rowWidth = Math.ceil(137.5 * width / 320);
let rowHeight = Math.ceil(254 * width / 320);
let rowMarginTop = Math.ceil(10 * width / 320);
let rowMarginHorizontal = (width - rowWidth * 2) / 3;
let styles = StyleSheet.create({
container: {
flex: 1,
width: width,
height: height - 64,
},
contentContainer:{
backgroundColor: 'white',
flexDirection: 'row',
flexWrap: 'wrap'
},
GoodsGroupHeader: {
flex: 1,
listContainer: {
width: width / 2,
},
footer: {
width: width,
height: 59,
borderColor: '#ededed',
borderTopWidth: 1,
flexDirection: 'row',
alignItems: 'center',
},
detailButton: {
position: 'absolute',
width: 100,
height: 44,
top: 7,
left: width - 15 - 100,
backgroundColor: '#d0021b',
borderRadius: 4,
alignItems: 'center',
},
detailButtonText: {
marginTop: 4,
height: 36,
color: 'white',
paddingTop: 10,
textAlign: 'center',
fontSize: 15,
},
favoriteButton: {
width: 100,
height: 40,
},
favIcon: {
width: 17,
height: 16,
marginLeft: 20,
marginRight: 0,
},
favText: {
fontSize: 13,
color: '#b0b0b0',
marginLeft: 5,
},
});
... ...
import React from 'react';
import ReactNative from 'react-native';
import ImmutablePropTypes from 'react-immutable-proptypes';
let {
requireNativeComponent,
View
} = ReactNative;
module.exports = requireNativeComponent('YH_GoodGoodsImagesView', null);
class GoodGoodsImagesView extends React.Component {
constructor(props) {
super(props);
this._onClick = this._onClick.bind(this);
}
_onClick(event: Event) {
if (!this.props.onClick) {
return;
}
this.props.onClick(event.nativeEvent);
}
render() {
return <YH_GoodGoodsImagesView {...this.props} items={this.props.items} />;
}
}
GoodGoodsImagesView.propTypes = {
items: React.PropTypes.arrayOf(
React.PropTypes.string
),
...View.propTypes // 包含默认的View的属性
};
let YH_GoodGoodsImagesView = requireNativeComponent('YH_GoodGoodsImagesView', GoodGoodsImagesView);
module.exports = GoodGoodsImagesView;
... ...
... ... @@ -7,5 +7,11 @@ export default keyMirror({
SET_SERVICE_HOST: null,
SET_CHANNEL: null,
SET_PRODUCT_SKN: null,
SET_PRODUCT_SKN: null,
GET_DETAIL_REQUEST: null,
GET_DETAIL_SUCCESS: null,
GET_DETAIL_FAILURE: null,
GET_SIMILAR_REQUEST: null,
GET_SIMILAR_SUCCESS: null,
GET_SIMILAR_FAILURE: null,
});
... ...
... ... @@ -46,11 +46,12 @@ function mapDispatchToProps(dispatch) {
class DetailContainer extends Component {
constructor(props) {
super(props);
this._onPressProduct = this._onPressProduct.bind(this);
}
componentDidMount() {
this.props.actions.getDetail();
this.props.actions.getSimilar();
}
componentWillUnmount() {
... ... @@ -68,13 +69,12 @@ class DetailContainer extends Component {
}
render() {
let {detail} = this.props;
return (
<View style={styles.container}>
<Detail
/>
</View>
<Detail
resource={detail}
onPressProduct={this._onPressProduct}
/>
);
}
}
... ...
... ... @@ -8,7 +8,12 @@ import helper from '../../../common/utils/helper';
const {
SET_PRODUCT_SKN,
GET_DETAIL_REQUEST,
GET_DETAIL_SUCCESS,
GET_DETAIL_FAILURE,
GET_SIMILAR_REQUEST,
GET_SIMILAR_SUCCESS,
GET_SIMILAR_FAILURE,
} = require('../../constants/actionTypes').default;
export function setProductSKN(product_skn) {
... ... @@ -17,3 +22,85 @@ export function setProductSKN(product_skn) {
payload: product_skn,
};
}
export function getDetail() {
return (dispatch, getState) => {
let {app, detail} = getState();
let {product_skn} = detail;
// if (!product_skn) {
// return ;
// }
dispatch(getDetailRequest());
return new DetailService(app.host).getDetailWithProductSKN(product_skn)
.then(json => {
console.log(product_skn);
console.log(json);
dispatch(getDetailSuccess(json));
})
.catch(error => {
dispatch(getDetailFailure(error));
});
};
}
export function getDetailRequest() {
return {
type: GET_DETAIL_REQUEST,
};
}
export function getDetailSuccess(json) {
return {
type: GET_DETAIL_SUCCESS,
payload: json
};
}
export function getDetailFailure(error) {
return {
type: GET_DETAIL_FAILURE,
payload: error
};
}
export function getSimilar() {
return (dispatch, getState) => {
let {app, detail} = getState();
let {product_skn} = detail;
// if (!product_skn) {
// return ;
// }
dispatch(getSimilarRequest());
return new DetailService(app.host).getSimilarListWithSKN(product_skn)
.then(json => {
console.log(product_skn);
console.log(json);
dispatch(getSimilarSuccess(json.product_list));
})
.catch(error => {
// console.log(product_skn);
// console.log(error);
dispatch(getSimilarFailure(error));
});
};
}
export function getSimilarRequest() {
return {
type: GET_SIMILAR_REQUEST,
};
}
export function getSimilarSuccess(json) {
return {
type: GET_SIMILAR_SUCCESS,
payload: json
};
}
export function getSimilarFailure(error) {
return {
type: GET_SIMILAR_FAILURE,
payload: error
};
}
... ...
... ... @@ -4,38 +4,16 @@ import {Record, List, Map} from 'immutable';
let InitialState = Record({
product_skn: '',
article: new (Record({
product: new (Record({
isFetching: false,
error: null,
data: null,
})),
author: new (Record({
isFetching: false,
error: null,
data: null,
})),
content: new (Record({
isFetching: false,
error: null,
data: List(),
})),
brand: new (Record({
isFetching: false,
error: null,
data: List(),
})),
otherArticle: new (Record({
isFetching: false,
error: null,
data: List(),
})),
weixin: new (Record({
similarList: new (Record({
isFetching: false,
error: null,
data: List(),
})),
goods_group_Filter: 0,
goods_group_y: 0,
});
export default InitialState;
... ...
... ... @@ -6,6 +6,12 @@ import Immutable, {Map} from 'immutable';
const {
SET_PRODUCT_SKN,
GET_DETAIL_REQUEST,
GET_DETAIL_SUCCESS,
GET_DETAIL_FAILURE,
GET_SIMILAR_REQUEST,
GET_SIMILAR_SUCCESS,
GET_SIMILAR_FAILURE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
... ... @@ -15,6 +21,30 @@ export default function detailReducer(state=initialState, action) {
case SET_PRODUCT_SKN: {
return state.set('product_skn', action.payload);
}
case GET_DETAIL_REQUEST: {
return state.setIn(['product', 'isFetching'], true);
}
case GET_DETAIL_SUCCESS: {
return state.setIn(['product', 'isFetching'], false)
.setIn(['product', 'data'], Immutable.fromJS(action.payload))
.setIn(['product', 'error'], null);
}
case GET_DETAIL_FAILURE: {
return state.setIn(['product', 'isFetching'], false)
.setIn(['product', 'error'], action.payload);
}
case GET_SIMILAR_REQUEST: {
return state.setIn(['similarList', 'isFetching'], true);
}
case GET_SIMILAR_SUCCESS: {
return state.setIn(['similarList', 'isFetching'], false)
.setIn(['similarList', 'data'], Immutable.fromJS(action.payload))
.setIn(['similarList', 'error'], null);
}
case GET_SIMILAR_FAILURE: {
return state.setIn(['similarList', 'isFetching'], false)
.setIn(['similarList', 'error'], action.payload);
}
}
return state;
... ...
... ... @@ -12,60 +12,18 @@ export default class DetailService {
this.api = new Request(baseURL);
}
// 获取资讯
async getArticle(article_id) {
async getDetailWithProductSKN(product_skn) {
//test
product_skn = '51148345';
this.api = new Request('http://dev-api.yohops.com:9999');
//
return await this.api.get({
url: '/guang/service/v2/article/getArticle',
body: {
article_id,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
// 获取作者信息
async getAuthor(author_id) {
return await this.api.get({
url: '/guang/service/v1/author/getAuthor',
body: {
author_id,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
// 获取资讯内容
async getArticleContent(article_id) {
return await this.api.get({
url: '/guang/service/v2/article/getArticleContent',
body: {
article_id,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
// 获取资讯相关的品牌
async getBrand(article_id) {
return await this.api.get({
url: '/guang/service/v2/article/getBrand',
url: '',
body: {
article_id,
method: 'app.product.goodDetail',
debug: 'XYZ',
product_skn
}
})
.then((json) => {
... ... @@ -76,50 +34,18 @@ export default class DetailService {
});
}
// 获取资讯相关的其它资讯
async getOtherArticle(article_id, tags, offset=0, limit=3) {
return await this.api.get({
url: '/guang/service/v2/article/getOtherArticle',
body: {
article_id,
tags,
offset,
limit,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async getSimilarListWithSKN(product_skn) {
async getWeixinPublic() {
return await this.api.get({
url: '',
body: {
method: 'app.resources.getSingleTemplate',
module: 'wechat',
key: 'guang_detail_wechat'
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
//test
product_skn = '51148345';
//
async productInfoBySkns(skns) {
return await this.api.get({
url: '',
body: {
method: 'app.search.li',
query: skns.join(' '),
limit: skns.length,
order: 's_t_desc',
method: 'app.search.findLike',
limit: 20,
product_skn
}
})
.then((json) => {
... ...