Showing 68 changed files with 4738 additions and 13 deletions

Too many changes to show.

To preserve performance only 68 of 68+ files are displayed.

... ... @@ -9,6 +9,7 @@ import BrandStore from './brandStore/BrandStore';
import Classify from './classify/Classify';
import Seckill from './seckill/Seckill';
import Installment from './installment/Installment';
import Guang from './guang/Guang';
// import Search from './search/Search';
export default function native(platform) {
... ... @@ -17,9 +18,11 @@ export default function native(platform) {
Classify(platform);
Seckill(platform);
Installment(platform);
Guang(platform);
Plustar(platform);
if (Platform.OS === 'ios') {
Community(platform);
Plustar(platform);
QRCode(platform);
} else {
... ...
... ... @@ -124,6 +124,7 @@ export default class CategoryList extends Component {
let currentCate=categoryList.get(this.props.currentChannelId).get(this.props.currentCateId);
subCateList=currentCate.get('sub').toArray();
}
return (
<View style={styles.container}>
<ListView
... ...
'use strict';
const _ = require('lodash');
/**
* 七牛图片路径处理
* @param {[string]} url
* @param {[string]} width
* @param {[string]} height
* @param {[string]} mode
* @param {int} quality
* @return {[string]}
*/
exports.image = (url, width, height, mode, quality) => {
mode = _.isNumber(mode) ? mode : 2;
url = url || '';
url = url.replace(/{width}/g, width).replace(/{height}/g, height).replace(/{mode}/g, mode);
if (url.indexOf('imageView2') > 0) {
quality = quality || 90;
url += '/q/' + quality;
}
return url;
};
\ No newline at end of file
... ...
export function urlAddParamOfType(url, type='0') {
if (!type) {
return url;
}
let strs= new Array();
let dataString = '';
if (url.indexOf('yohobuy=') !== -1) {
strs = url.split("yohobuy=");
if (strs.length == 1) {
dataString = strs[0];
} else {
dataString = strs[1];
}
} else {
strs = url.split("yohobuy=");
if (strs.length == 1) {
dataString = strs[0];
} else {
dataString = strs[1];
}
}
var obj = JSON.parse(dataString); //由JSON字符串转换为JSON对象
obj.params.type = type + ''; // params增加type参数
let totalUrlWithType = "yohobuy=" + JSON.stringify(obj);
if (strs.length > 1) {
totalUrlWithType = strs[0] + totalUrlWithType;
}
return totalUrlWithType;
}
\ No newline at end of file
... ...
'use strict';
import React from 'react';
import ReactNative, {
AppRegistry,
Platform,
} from 'react-native';
import {
Provider,
connect
} from 'react-redux';
import configureStore from './store/configureStore';
import {Record, List, Map} from 'immutable';
import appInitialState from './reducers/app/appInitialState';
import listInitialState from './reducers/list/listInitialState';
import detailInitialState from './reducers/detail/detailInitialState';
import ListContainer from './containers/ListContainer';
import DetailContainer from './containers/DetailContainer';
import {
setPlatform,
setHost,
setServiceHost,
} from './reducers/app/appActions';
import {
setArticleId,
} from './reducers/detail/detailActions';
import {
setListType,
setAuthorId,
setTag,
} from './reducers/list/listActions';
function getInitialState() {
const _initState = {
app: (new appInitialState()),
list: (new listInitialState()),
detail: (new detailInitialState()),
};
return _initState;
}
export default function native(platform) {
let YH_Guang = React.createClass({
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;
console.log('-------------');
console.log(this.props);
if (type == 'list') {
store.dispatch(setListType(this.props.listType));
store.dispatch(setAuthorId(this.props.authorId));
store.dispatch(setTag(this.props.tag));
return (
<Provider store={store}>
<ListContainer />
</Provider>
);
} else {
store.dispatch(setArticleId(this.props.articleId));
return (
<Provider store={store}>
<DetailContainer />
</Provider>
);
}
}
});
AppRegistry.registerComponent('YH_Guang', () => YH_Guang);
}
... ...
'use strict';
import React, {Component} from 'react';
import Immutable, {Map} from 'immutable';
import DetailBrand from './DetailBrand';
import OtherArticle from './OtherArticle';
import Tags from './Tags';
import SingleImage from './SingleImage';
import DetailText from './DetailText';
import GoodsCell from './GoodsCell';
import Header from './Header';
import MoreLink from './MoreLink';
import GoodsGroupHeader from './GoodsGroupHeader';
import GoodsGroupList from './GoodsGroupList';
import SmallPic from './SmallPic';
import Weixin from './Weixin';
import LoadingIndicator from '../../../common/components/LoadingIndicator';
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.renderRow = this.renderRow.bind(this);
this.renderHeader = this.renderHeader.bind(this);
this.scrollTo = this.scrollTo.bind(this);
this.listView = null;
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
onLayout(rowID, e) {
let {y} = e.nativeEvent.layout;
this.props.onPressGoodY(y);
}
scrollTo(){
let {goods_group_y} = this.props.resource;
this.listView.scrollTo({x: 0, y: goods_group_y, animated: false});
}
renderHeader() {
let {resource} = this.props;
let {author,article} = resource;
return(
<Header resource={{author,article}} onPressAuthor={this.props.onPressAuthor} />
);
}
renderRow(rowData,sectionID,rowID,highlightRow) {
if (sectionID == 'detailList') {
let template_name = rowData.get('template_name');
if (template_name == 'text') {
return (
<DetailText resource={rowData} onPressLink={this.props.onPressLink}/>
);
}else if (template_name == 'single_image') {
return (
<SingleImage resource={rowData}/>
);
}else if (template_name == 'small_pic') {
return (
<SmallPic resource={rowData}/>
);
}else if (template_name == 'goods_group') {
let {resource} = this.props;
let {goods_group_Filter} = resource;
let data = rowData.get('productList')?rowData.get('productList').toArray():null;
let list = data?data[goods_group_Filter]:null;
return (
<View style={styles.GoodsGroupHeader} onLayout={this.onLayout.bind(this, rowID)}>
<GoodsGroupHeader resource={rowData} goods_group_Filter={goods_group_Filter} onPressFilter= {this.props.onPressFilter} scrollTo={this.scrollTo}/>
<GoodsGroupList resource={list} onPressProduct={this.props.onPressProduct}/>
</View>
);
}else if (template_name == 'goods') {
return (
<GoodsCell resource={rowData} onPressProduct={this.props.onPressProduct}/>
);
}else if (template_name == 'link') {
return (
<MoreLink resource={rowData} onPressMoreLink={this.props.onPressMoreLink}/>
);
}
}else if (sectionID == 'weixin') {
return (
<Weixin resource={rowData} onPressWeixin={this.props.onPressWeixin}/>
);
}else if (sectionID == 'detailBrand') {
return (
<DetailBrand resource={rowData} onPressBrand={this.props.onPressBrand}/>
);
}else if (sectionID == 'detailOtherArticle') {
return (
<OtherArticle resource={rowData} onPressArticle={this.props.onPressArticle}/>
);
} else if (sectionID == 'tags'){
return (
<Tags resource={rowData} onPressTag={this.props.onPressTag}/>
);
}
return null;
}
render() {
let {resource} = this.props;
let {
articleId,
article,
author,
content,
brand,
otherArticle,
weixin,
goods_group_Filter,
} = resource;
let list = content?content.get('data'):[];
let dataSource = {
detailList: list.size?list.toArray():[],
weixin: [weixin],
detailBrand: [brand],
tags:[article],
detailOtherArticle: [otherArticle],
};
let isFetching = content.isFetching || article.isFetching || author.isFetching;
return (
<View style={styles.container}>
{!isFetching?<ListView
ref={(ref)=>this.listView=ref}
contentContainerStyle={styles.contentContainer}
enableEmptySections={true}
showsVerticalScrollIndicator={false}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this.renderRow}
renderHeader={this.renderHeader}
/>:null}
<LoadingIndicator
isVisible={isFetching}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
contentContainer:{
},
GoodsGroupHeader: {
flex: 1,
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class DetailBrand extends React.Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1.key != r2.key,
});
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
_renderRow(rowData, sectionID, rowID) {
let {url} = rowData;
if ((rowID+1)%4 == 0) {
return (
<TouchableOpacity style={styles.lastCell} onPress={()=>{this.props.onPressBrand&&this.props.onPressBrand(url)}}>
<Image style={styles.cellImg} source={{uri:rowData.thumb}}/>
<Text style={styles.cellTitle}>{rowData.name}</Text>
</TouchableOpacity>
);
} else {
return (
<TouchableOpacity style={styles.cell} onPress={()=>{this.props.onPressBrand&&this.props.onPressBrand(url)}}>
<Image style={styles.cellImg} source={{uri:rowData.thumb}}/>
<Text style={styles.cellTitle}>{rowData.name}</Text>
</TouchableOpacity>
);
}
}
render() {
let {data} = this.props.resource.toJS();
if (!data.length) {
return null;
}
return(
<View style={styles.container}>
<View style={styles.headerView}>
<Text style={styles.headerText}>相关品牌</Text>
</View>
<ListView
contentContainerStyle={styles.brandContainer}
enableEmptySections={true}
initialListSize={data.length}
dataSource={this.dataSource.cloneWithRows(data)}
renderRow={this._renderRow}
scrollEnabled={false}
scrollsToTop={false}
/>
</View>
);
return null;
}
};
let {width, height} = Dimensions.get('window');
let cellWidth = width/4.0;
let styles = StyleSheet.create({
container: {
backgroundColor: '#f0f0f0',
},
headerView: {
backgroundColor: 'white',
borderColor: 'rgb(215, 215, 215)',
marginLeft: 15,
marginRight: 15,
marginTop: 15,
height: 44,
borderTopWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
},
headerText: {
backgroundColor: 'white',
fontSize: 18,
color: 'rgb(215, 215, 215)',
textAlign: 'center',
paddingTop: 13,
flex: 1,
},
brandContainer: {
backgroundColor: 'white',
borderColor: 'rgb(215, 215, 215)',
borderTopWidth: 1,
borderBottomWidth: 1,
flexDirection: 'row',
flexWrap: 'wrap',
width: width,
paddingBottom: 15,
},
cell: {
width: cellWidth,
height: 75,
backgroundColor: 'white',
marginTop: 15,
flexDirection: 'column',
borderColor: 'rgb(215, 215, 215)',
borderRightWidth: 1,
justifyContent: 'space-between',
},
lastCell: {
width: cellWidth,
height: 75,
backgroundColor: 'white',
marginTop: 15,
flexDirection: 'column',
justifyContent: 'space-between',
},
cellImg: {
backgroundColor: 'white',
marginLeft: (cellWidth-55)/2,
width: 55,
height: 55,
},
cellTitle: {
backgroundColor: 'white',
fontSize: 11,
color: 'rgb(215, 215, 215)',
textAlign: 'center',
marginLeft: 0,
marginRight: 0,
}
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
View,
Dimensions,
TouchableOpacity,
WebView,
} = ReactNative;
const BODY_TAG_PATTERN = /\<\/ *body\>/;
var script = `
;(function() {
var wrapper = document.createElement("div");
wrapper.id = "height-wrapper";
while (document.body.firstChild) {
wrapper.appendChild(document.body.firstChild);
}
document.body.appendChild(wrapper);
var i = 0;
function updateHeight() {
document.title = wrapper.clientHeight;
window.location.hash = ++i;
}
updateHeight();
window.addEventListener("load", function() {
updateHeight();
setTimeout(updateHeight, 1000);
});
window.addEventListener("resize", updateHeight);
}());
`;
const style = `
<style>
body, html, #height-wrapper {
margin: 0;
padding: 0;
}
#height-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
}
</style>
<script>
${script}
</script>
`;
const codeInject = (html) => html.replace(BODY_TAG_PATTERN, style + "</body>");
export default class DetailText extends React.Component {
constructor(props) {
super(props);
this.handleNavigationChange = this.handleNavigationChange.bind(this);
this.shouldStartLoadWithRequest = this.shouldStartLoadWithRequest.bind(this);
this.state = {
realContentHeight : 0,
};
}
shouldComponentUpdate(nextProps,nextState){
if (Immutable.is(nextProps.resource, this.props.resource) && nextState.realContentHeight==this.state.realContentHeight) {
return false;
} else {
return true;
}
}
handleNavigationChange(navState) {
let heightT = parseInt(navState.title, 10) || 0; // turn NaN to 0
this.setState({
realContentHeight: heightT,
});
}
shouldStartLoadWithRequest(event) {
if (event.navigationType == 'click') {
this.props.onPressLink && this.props.onPressLink(event.url);
return false;
}
return true;
}
render() {
let {resource} = this.props;
let template_name = resource.get('data');
let text = template_name.get('text');
text = '<html><style type="text/css">img {max-width: 100%;}</style><body>' + text + '</body></html>'
let hasLink = false;
if (text.indexOf('<a href=') >= 0) {
hasLink = true;
}
return (
<View style={{width: width,height: this.state.realContentHeight,backgroundColor: 'white'}}>
<WebView style={{width: width-40,height: this.state.realContentHeight,backgroundColor: 'white',marginRight: 20,marginLeft: 20}}
source= {{html: codeInject(text)}}
scrollEnabled={false}
javaScriptEnabled={true}
decelerationRate="normal"
domStorageEnabled={true}
onNavigationStateChange={this.handleNavigationChange}
onShouldStartLoadWithRequest={hasLink?this.shouldStartLoadWithRequest:null}
>
</WebView>
</View>
);
return null;
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
import BrandProductListCell from '../../../common/components/ListCell/ProductListCell';
import DeleteLineText from '../../../common/components/DeleteLineText';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class GoodsCell extends React.Component {
constructor(props) {
super(props);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
this.renderHeader = this.renderHeader.bind(this);
this.renderRow = this.renderRow.bind(this);
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
renderRow(rowData, sectionID, rowID, highlightRow) {
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}
/>
);
}
renderHeader() {
return(
<View style={styles.titleB}>
<View style={{width: width,height: 0.5,backgroundColor: '#e5e5e5',}}/>
<View style={styles.title}>
<Text style={styles.text}>相关推荐</Text>
</View>
</View>
);
}
render() {
let {resource} = this.props;
let list = resource.get('productList');
if (!list || list.size == 0) {
return null;
}
if (list.size == 1) {
let backgroundWidth = width;
let backgroundHeight = 40 + 80;
let list0 = list.toJS();
let obj = list0[0];
let product_name = obj.product_name;
let default_images = obj.default_images;
let isGlobalProduct = obj.is_global && obj.is_global == 'Y'; // 是否全球购商品
let salePrice = 0; // 售卖价
let originPrice = 0; // 原价
let salePriceStr = ''; // 拼接的售卖价
let originPriceStr = ''; // 拼接的原价
let showOriginPrice = true; // 是否显示原价
let salePriceColor = '#d0021b'; // 不显示原价时,售卖价颜色
if (isGlobalProduct) {
salePrice = parseFloat(obj.final_price);
originPrice = parseFloat(obj.orign_price);
salePriceStr = obj.formart_final_price;
originPriceStr = obj.formart_orign_price;
} else {
salePrice = parseFloat(obj.sales_price);
originPrice = parseFloat(obj.market_price);
salePriceStr = '¥' + salePrice.toFixed(2);
originPriceStr = '¥' + originPrice.toFixed(2);
}
if (!originPrice || (salePrice == originPrice)) {
showOriginPrice = false;
salePriceColor = '#444444';
}
return(
<View style={{width: backgroundWidth, height:backgroundHeight, backgroundColor:'white'}}>
<View style={styles.titleB}>
<View style={{width: width,height: 0.5,backgroundColor: '#e5e5e5',}}/>
<View style={styles.title}>
<Text style={styles.text}>相关推荐</Text>
</View>
</View>
<View style={styles.single_View}>
<Image source={{uri: default_images}} style={styles.timeThumb} resizeMode={'contain'}></Image>
<View style={styles.nameView}>
<Text style={styles.name}>{product_name}</Text>
<View style={styles.salesView}>
<View style={styles.priceContainer}>
<Text style={[styles.nowPrice, {color: salePriceColor}]} numberOfLines={1}>{salePriceStr}</Text>
{showOriginPrice ? <DeleteLineText
style={styles.oldPriceContainer}
textStyle={styles.oldPrice}
lineStyle={styles.deleteLine}
text={originPriceStr}
/> : null}
</View>
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.props.onPressProduct && this.props.onPressProduct(Immutable.fromJS(obj));
}}>
<View style={styles.button}>
<Text style={styles.b}>查看详情</Text>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</View>
);
}else {
let backgroundWidth = width;
let backgroundHeight = 40 + Math.ceil(list.size / 2) * (rowHeight+rowMarginHorizontal) + 20;
return(
<View style={{width: backgroundWidth, height:backgroundHeight, backgroundColor:'white'}}>
<ListView
contentContainerStyle={styles.contentContainer}
initialListSize={Math.ceil(list.size)}
dataSource={this.dataSource.cloneWithRows(list.toArray())}
renderHeader={this.renderHeader}
enableEmptySections={true}
renderRow={this.renderRow}
scrollEnabled={false}
scrollsToTop={false}
/>
</View>
);
}
}
};
let {width, height} = Dimensions.get('window');
let rowWidth = Math.ceil(137.5 * width / 320);
let rowMarginHorizontal = (width - rowWidth * 2) / 3;
const DEVICE_WIDTH_RATIO = width / 320;
let rowHeight = Math.ceil(254 * DEVICE_WIDTH_RATIO);
let styles = StyleSheet.create({
listContainer: {
width: width / 2,
},
contentContainer:{
flexDirection: 'row',
flexWrap: 'wrap',
},
titleB: {
alignItems: 'center',
justifyContent: 'center',
height: 40,
width:width,
backgroundColor: 'white',
},
title: {
alignItems: 'center',
justifyContent: 'center',
height: 39.5,
width:width,
},
text: {
color: 'rgb(215, 215, 215)',
},
single_View: {
height: 80,
width:width,
backgroundColor: 'white',
flexDirection: 'row',
},
nameView: {
height: 80,
width:width,
backgroundColor: 'white',
},
name: {
marginTop: 20,
marginLeft:15,
height: 20,
width:width,
backgroundColor: 'white',
},
salesView: {
flexDirection: 'row',
justifyContent: 'space-between',
height: 20,
width:width - 80,
backgroundColor: 'white',
},
button: {
height: 20,
width: 60,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
borderColor: 'black',
borderWidth: 1,
borderRadius: 2,
},
timeThumb: {
marginTop: 10,
marginLeft: 20,
height: 50,
width:40,
backgroundColor: 'white',
},
priceContainer: {
marginLeft: 15,
flexDirection: 'row',
marginTop: 1,
alignItems: 'center',
justifyContent: 'center',
},
nowPrice: {
fontSize: 12,
color: '#d0021b',
},
oldPriceContainer: {
flexDirection: 'row',
marginLeft: 5,
},
oldPrice: {
fontSize: 12,
color: '#b0b0b0',
height: 16,
},
deleteLine: {
position: 'absolute',
top: (16 / 2) - 0.8,
left: 0,
right: 0,
height: 1,
backgroundColor: '#b0b0b0',
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import {getSlicedUrl} from '../../../classify/utils/Utils';
const {
View,
Text,
ListView,
TouchableOpacity,
Dimensions,
StyleSheet,
Image,
} = ReactNative;
export default class GoodsGroupHeader extends React.Component {
constructor(props) {
super (props);
this._renderRow = this._renderRow.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1.key != r2.key,
});
this.selectedIndex = 0;
}
_renderRow(rowData, sectionID, rowID) {
let cover = rowData.get('cover');
let url = getSlicedUrl(cover.get('cover'), 640, 640, 2);
let borderWidth = 0;
if (this.selectedIndex == rowID) {
borderWidth = 1;
}
return (
<View style={{backgroundColor: 'white'}}>
<View key={'row' + rowID} style={styles.rowContainer}>
<TouchableOpacity onPress={() => {
if (this.selectedIndex == rowID) {
return;
}
this.props.scrollTo && this.props.scrollTo();
this.props.onPressFilter && this.props.onPressFilter(rowID);
}}>
<View style={{width: itemW - 20,height: itemH - 20,borderColor: 'black',borderWidth: borderWidth}}>
<Image source={{uri: url}} style={{width: itemW - 20 - 2*borderWidth,height: itemH - 20 - 2*borderWidth,backgroundColor:'white',}} resizeMode={'contain'}>
<View style={{width: 10,height: 10,marginLeft: itemW - 20 - 2*borderWidth - 12,marginTop: itemH - 20 - 2*borderWidth - 12,backgroundColor: 'white',borderRadius: 5}}>
</View>
</Image>
</View>
</TouchableOpacity>
</View>
</View>
);
}
render() {
let {resource,goods_group_Filter} = this.props;
this.selectedIndex = goods_group_Filter;
let list = resource?resource.get('data'):null;
if (!list || list.size == 0) {
return null;
}
let arrow_up_offset = ((Math.ceil(this.selectedIndex)+1) *2 - 1)*10 + (itemW-20) * ((Math.ceil(this.selectedIndex)+1) - 0.5) - 8;
return (
<View style={[styles.container]}>
<ListView
contentContainerStyle={[styles.contentContainer]}
enableEmptySections={true}
dataSource={this.dataSource.cloneWithRows(list.toArray())}
renderRow={this._renderRow}
scrollEnabled={false}
scrollsToTop={false}
/>
<View style={{width: width,height: 10,backgroundColor: 'white',}}>
</View>
<View style={{width: width,height: 9,backgroundColor: 'white'}}>
<View style={styles.line}/>
<Image source={require('../../image/global_arrow_up.png')} style={{marginLeft: arrow_up_offset,width: 16,height: 9,backgroundColor: 'transparent'}}>
</Image>
</View>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let itemW = Math.ceil(width / 5);
let itemH = itemW * (50/40);
let styles = StyleSheet.create({
container: {
width: width,
height: itemH + 20,
backgroundColor:'white',
},
contentContainer: {
flexDirection: 'row',
},
rowContainer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
width: itemW,
height: itemH + 20,
backgroundColor:'white',
},
line: {
position: 'absolute',
marginTop:8,
width: width,
height: 1,
backgroundColor: '#e0e0e0',
}
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
import BrandProductListCell from '../../../common/components/ListCell/ProductListCell';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class GoodsGroupList extends React.Component {
constructor(props) {
super(props);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
this.renderRow = this.renderRow.bind(this);
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
renderRow(rowData, sectionID, rowID, highlightRow) {
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}
/>
);
}
render() {
let {resource} = this.props;
let list = resource?resource.get('list'):null;
if (!list || list.size == 0) {
return null;
}
let backgroundWidth = width;
let backgroundHeight = 40 + Math.ceil(list.size / 2) * (rowHeight+rowMarginHorizontal) + 20;
return(
<View style={{width: backgroundWidth, height:backgroundHeight, backgroundColor:'white'}}>
<ListView
contentContainerStyle={styles.contentContainer}
initialListSize={Math.ceil(list.size)}
dataSource={this.dataSource.cloneWithRows(list.toArray())}
enableEmptySections={true}
renderRow={this.renderRow}
scrollEnabled={false}
scrollsToTop={false}
/>
</View>
);
}
};
let {width, height} = Dimensions.get('window');
let rowWidth = Math.ceil(137.5 * width / 320);
let rowMarginHorizontal = (width - rowWidth * 2) / 3;
const DEVICE_WIDTH_RATIO = width / 320;
let rowHeight = Math.ceil(254 * DEVICE_WIDTH_RATIO);
let styles = StyleSheet.create({
listContainer: {
width: width / 2,
},
contentContainer:{
flexDirection: 'row',
flexWrap: 'wrap',
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class Header extends React.Component {
constructor(props) {
super(props);
this.getStrSize = this.getStrSize.bind(this);
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
getStrSize(str) {
let realLength = 0, len = str.length, charCode = -1;
for (let i = 0; i < len; i++) {
charCode = str.charCodeAt(i);
if (charCode >= 0 && charCode <= 128) realLength += 1;
else realLength += 2;
}
return realLength;
}
render() {
let {resource} = this.props;
let {author,article} = resource;
let author_data = author.get('data');
let article_data = article.get('data');
let hasData = true;
if (!author_data || !article_data) {
hasData = false;
}
let author_desc = hasData?author_data.get('author_desc'):'';
let author_avatar = hasData?author_data.get('avatar'):'';
let author_name = hasData?author_data.get('name'):'';
let author_url = hasData?author_data.get('url'):'';
let num = this.getStrSize(author_desc);
let article_title = hasData?article_data.get('article_title'):'';
let pageViews = hasData?article_data.get('pageViews'):'99999';
let publishTime = hasData?article_data.get('publishTime'):'12月12日 12:00';
return(
<View style={styles.contentContainer}>
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.props.onPressAuthor && this.props.onPressAuthor(author_url);
}}>
<View style={styles.header}>
<Image source={{uri: author_avatar}} style={styles.thumb}></Image>
<Text style={styles.name}>{author_name}</Text>
{(num > 25)?null:<Text style={styles.desc} numberOfLines={1}>{author_desc}</Text>}
</View>
</TouchableOpacity>
{(num > 25)?<Text style={styles.desc2}>{author_desc}</Text>:null}
<View style={{width: width,height: 0.5,backgroundColor: '#e5e5e5',}}/>
<Text style={styles.article_title}>{article_title}</Text>
<View style={styles.time}>
<Image source={require('../../image/time_icon.png')} style={styles.timeThumb}resizeMode={'contain'}></Image>
<Text style={styles.text}>{publishTime}</Text>
<Image source={require('../../image/shared_view_icon.png')} style={styles.timeThumb}resizeMode={'contain'}></Image>
<Text style={styles.text}>{pageViews}</Text>
</View>
<View style={{width: width,height: 10}}/>
</View>
);
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
contentContainer: {
width:width,
backgroundColor: 'white',
},
header: {
flexDirection: 'row',
height: 47.5,
width:width,
alignItems: 'center',
},
thumb: {
width: 25,
height: 25,
marginLeft: 20,
borderRadius: 12,
},
name: {
marginLeft: 20,
},
desc: {
marginLeft: 20,
color: '#e5e5e5',
},
desc2: {
marginLeft: 20,
color: '#e5e5e5',
width: width,
fontSize: 15,
minHeight: 40,
},
article_title: {
marginLeft: 20,
marginRight: 10,
fontSize: 23,
fontWeight: 'bold',
lineHeight: 35,
},
time: {
flexDirection: 'row',
height: 20,
width:width,
alignItems: 'center',
marginTop: 5,
},
timeThumb: {
marginLeft: 20,
height: 12,
width:20,
},
text: {
marginLeft: 5,
fontSize: 12,
color: 'gray'
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class MoreLink extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
render() {
let {resource} = this.props;
let data = resource.get('data');
let url = data?data.get('url'):null;
return(
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.props.onPressMoreLink && this.props.onPressMoreLink(url);
}}>
<View style={styles.titleB}>
<View style={{width: width,height: 0.5,backgroundColor: '#e5e5e5',}}/>
<View style={styles.title}>
<Text style={styles.text}>更多商品</Text>
<Image source={require('../../image/community_enter_normal.png')} style={styles.timeThumb}></Image>
</View>
<View style={{width: width,height: 0.5,backgroundColor: '#e5e5e5',}}/>
</View>
</TouchableOpacity>
);
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
titleB: {
alignItems: 'center',
justifyContent: 'center',
height: 40,
width:width,
backgroundColor: 'white',
},
title: {
flexDirection: 'row',
alignItems: 'center',
marginLeft: 20,
height: 39,
width:width - 20,
justifyContent: 'space-between',
backgroundColor: 'white',
},
text: {
color: 'rgb(215, 215, 215)',
},
timeThumb: {
height: 40,
width:40,
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class OtherArticle extends React.Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
shouldComponentUpdate(nextProps){
if (Immutable.is(nextProps.resource, this.props.resource)) {
return false;
} else {
return true;
}
}
_renderRow(rowData, sectionID, rowID) {
let {url} = rowData;
return (
<TouchableOpacity style={styles.cell} onPress={()=>{this.props.onPressArticle&&this.props.onPressArticle(rowData.get('url'))}}>
<Image style={styles.cellImage} source={{uri:rowData.get('thumb')}}/>
<View style={styles.cellContent}>
<Text style={styles.cellTitle} numberOfLines={2}>
{rowData.get('title')}
</Text>
<View style={styles.bottomContent}>
<Image source={require('../../image/time_icon.png')} style={styles.clockIcon}/>
<Text style={styles.timeText}>{rowData.get('publishTime')}</Text>
</View>
</View>
</TouchableOpacity>
);
}
render() {
let data = this.props.resource.get('data').toArray();
return(
<View style={styles.container}>
<View style={styles.headerView}>
<Text style={styles.headerText}>相关文章</Text>
</View>
<ListView
style={styles.articleContainer}
enableEmptySections={true}
initialListSize={data.size}
dataSource={this.dataSource.cloneWithRows(data)}
renderRow={this._renderRow}
scrollEnabled={false}
scrollsToTop={false}
/>
</View>
);
return null;
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
backgroundColor: '#f0f0f0',
},
headerView: {
backgroundColor: 'white',
borderColor: 'rgb(215, 215, 215)',
marginLeft: 15,
marginRight: 15,
marginTop: 15,
height: 44,
borderTopWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
},
headerText: {
backgroundColor: 'white',
fontSize: 18,
color: 'rgb(215, 215, 215)',
textAlign: 'center',
paddingTop: 13,
flex: 1,
},
articleContainer: {
backgroundColor: 'white',
borderColor: 'rgb(215, 215, 215)',
borderTopWidth: 1,
paddingBottom: 17,
},
cell: {
backgroundColor: 'white',
paddingBottom: 3,
flexDirection: 'row',
},
cellImage: {
width: 106,
height: 67,
top: 17,
left: 17,
backgroundColor: 'gray',
},
cellContent: {
left: 34,
marginTop: 17,
right: 17,
flexDirection: 'column',
backgroundColor: 'white',
justifyContent: 'space-between',
width: width-106-51,
},
cellTitle: {
fontSize: 15,
color: 'rgb(51, 51, 51)',
marginTop: 2,
marginLeft: 0,
backgroundColor: 'white'
},
bottomContent: {
left:0,
right: 0,
height: 16,
backgroundColor: 'white',
flexDirection: 'row',
},
clockIcon: {
top: 3,
width: 11,
height: 11,
},
timeText: {
backgroundColor: 'white',
fontSize: 10,
color: 'rgb(215, 215, 215)',
top: 3,
left: 3,
}
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class SingleImage extends React.Component {
constructor(props) {
super(props);
this.state = {
width: Dimensions.get('window').width,
height: 0,
};
}
shouldComponentUpdate(nextProps,nextState){
if (Immutable.is(nextProps.resource, this.props.resource) && nextState.height==this.state.height) {
return false;
} else {
return true;
}
}
componentDidMount() {
let {resource} = this.props;
let template_name = resource.get('data');
let src = template_name.get('src');
Image.getSize(src, (width, height) => {
if (this.imageView) {
this.setState({width, height});
}
});
}
render() {
let {resource} = this.props;
let template_name = resource.get('data');
let src = template_name.get('src');
return (
<Image
ref={(ref)=>this.imageView=ref}
source={{uri: src}}
style={{width: screenWidth,height: (this.state.height/this.state.width)*screenWidth}}
/>
);
}
};
let screenWidth = Dimensions.get('window').width;
let imageView = null;
let styles = StyleSheet.create({
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class SmallPic extends React.Component {
constructor(props) {
super(props);
this.state = {
width0: 1,
width1: 1,
height0: 1,
height1: 1,
};
}
componentDidMount() {
let {resource} = this.props;
let data = resource.get('data')?resource.get('data').toJS():null;
let url1 = data?data[0].src:null;
let url2 = data?data[1].src:null;
Image.getSize(url1, (width, height) => {
this.setState({width0 : width, height0 : height});
});
Image.getSize(url2, (width, height) => {
this.setState({width1 : width, height1 : height});
});
}
render() {
let {resource} = this.props;
let data = resource.get('data')?resource.get('data').toJS():null;
let url1 = data?data[0].src:null;
let url2 = data?data[1].src:null;
if (!url1 || !url2) {
return null;
}
return (
<View style={styles.Small_pic}>
<Image source={{uri: url1}} style={{width: width/2,height: (width/2)*(this.state.height0/this.state.width0)}} ></Image>
<Image source={{uri: url2}} style={{width: width/2,height: (width/2)*(this.state.height1/this.state.width1)}} ></Image>
</View>
);
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
Small_pic: {
flexDirection: 'row',
flex: 1,
backgroundColor: 'white'
},
thumb: {
}
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class Tags extends React.Component {
constructor(props) {
super(props);
}
render() {
let {resource} = this.props;
let data = resource.get('data') ? resource.get('data').toJS() : null;
let tags = data ? data.tags : [];
if (!tags || !tags.length) {
return null;
}
return (
<View style={styles.container}>
<Image style={styles.tagImage}/>
<View style={styles.tagContainer}>
{tags.map((value, i) => {
return (
<Text
key={i}
style={styles.tag}
onPress={()=>{
this.props.onPressTag && this.props.onPressTag(value.url)
}}
>
{value.name}
</Text>
);
})}
</View>
</View>
);
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flexDirection: 'row',
borderColor: 'rgb(215, 215, 215)',
borderBottomWidth: 1,
paddingBottom: 0,
},
tagImage: {
backgroundColor: 'gray',
top: 20,
left: 10,
width: 25,
height: 25,
marginRight: 0,
},
tagContainer: {
width: width - 45,
paddingBottom: 20,
marginLeft: 25,
flexWrap: 'wrap',
flexDirection: 'row',
},
tag: {
fontSize: 18,
color: 'black',
marginLeft: 0,
marginRight: 13,
marginTop: 20,
marginBottom: 0,
height: 20,
textDecorationLine:'underline'
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
import {getSlicedUrl} from '../../../classify/utils/Utils';
const {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
Dimensions,
TouchableOpacity,
} = ReactNative;
export default class Weixin extends React.Component {
constructor(props) {
super(props);
}
render() {
let {resource} = this.props;
let data = resource.get('data') ? resource.get('data').toJS() : null;
if (!data) {
return null;
}
let url1 = data.length>0?data[0].src:null;
let url2 = data.length>1?data[1].src:null;
let text1 = data.length>0?data[0].wechat_id:null;
let text2 = data.length>1?data[1].wechat_id:null;
if (!url1 || !url2) {
return null;
}
let url3 = getSlicedUrl(url1, 640, 640, 2);
let url4 = getSlicedUrl(url2, 640, 640, 2);
return (
<View style={styles.SmallImage}>
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.props.onPressWeixin && this.props.onPressWeixin(text1);
}}>
<View style={{width: width/2-30,height: 100,marginLeft: 20,backgroundColor:'transparent',marginTop: 20,marginBottom: 20}}>
<Image source={{uri: url3}} style={{width: width/2-30,height: 100,backgroundColor:'transparent'}} ></Image>
</View>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.props.onPressWeixin && this.props.onPressWeixin(text2);
}}>
<View style={{width: width/2-30,height: 100,marginLeft: 10,backgroundColor:'transparent',marginTop: 20,marginBottom: 20}}>
<Image source={{uri: url4}} style={{width: width/2-30,height: 100,marginLeft: 10,backgroundColor:'transparent'}} ></Image>
</View>
</TouchableOpacity>
</View>
);
}
};
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
SmallImage: {
flexDirection: 'row',
flex: 1,
backgroundColor: '#e5e5e5'
},
thumb: {
}
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
InteractionManager,
Platform,
} from 'react-native';
export default class List extends Component {
constructor(props) {
super(props);
}
render() {
let resource = this.props.resource;
console.log('xxxxxx');
console.log(resource);
if (!resource) {
return null;
}
return (
<View style={styles.headerContent}>
<Image style={styles.avatar} source={{uri:resource.avatar}}/>
<View style={styles.headerRight}>
<Text style={styles.title}>{resource.name}</Text>
<Text style={styles.subTitle}>{resource.author_desc?resource.author_desc:''}</Text>
</View>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
headerContent: {
height: 104,
width: width,
backgroundColor: 'white',
flexDirection: 'row',
borderColor: 'rgb(215, 215, 215)',
borderTopWidth: 1,
},
avatar: {
marginTop: 20,
marginLeft: 15,
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: 'gray',
},
headerRight: {
marginTop: 24,
marginLeft: 20,
flexDirection: 'column',
marginRight: 0,
backgroundColor: 'white',
height: 45,
width: width - 100,
justifyContent: 'space-between',
},
title: {
fontSize: 16,
color: 'rgb(83, 83, 83)',
textAlign: 'left',
width: width - 100,
},
subTitle: {
fontSize: 13,
color: 'rgb(215, 215, 215)',
textAlign: 'left',
width: width - 100,
}
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
InteractionManager,
Platform,
} from 'react-native';
import HeaderCell from './HeaderCell';
import ListCell from './ListCell';
import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator';
import LoadingIndicator from '../../../common/components/LoadingIndicator';
export default class List extends Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.renderHeader = this.renderHeader.bind(this);
this.state = {
author: null,
};
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
}
componentDidMount() {
let {list} = this.props.data.toJS().articles;
if (list&&list.length) {
let author = list[0].author;
this.setState({author});
}
}
componentWillReceiveProps(nextProps){
let {list} = nextProps.data.toJS().articles;
if (list&&list.length) {
let author = list[0].author;
this.setState({author});
}
}
renderHeader() {
if (this.props.data.type == 'editor') {
return(
<HeaderCell resource={this.state.author}/>
);
} else {
return null;
}
}
renderRow(rowData,sectionID,rowID) {
let type = this.props.data.get('type');
return(
<ListCell
resource={rowData}
type={type}
onPressCell={this.props.onPressCell}
onPressShare={this.props.onPressShare}
onPressLike={this.props.onPressLike}
onPressHeader={this.props.onPressHeader}
/>
);
}
render() {
let {list,isFetching} = this.props.data.toJS().articles;
if (!list||!list.length) {
return null;
}
return (
<View style={styles.container}>
<ListView
contentContainerStyle={styles.contentContainer}
enableEmptySections={true}
showsVerticalScrollIndicator={false}
dataSource={this.dataSource.cloneWithRows(list)}
renderRow={this.renderRow}
renderHeader={this.renderHeader}
onEndReached={() => {
if (list && list.length) {
this.props.onEndReached && this.props.onEndReached();
}
}}
renderFooter={()=>{
if (list && list.length && isFetching) {
return <LoadMoreIndicator
isVisible={isLoadingMore}
animating={true}
/>;
} else {
return null;
}
}}
/>
<LoadingIndicator
isVisible={isFetching}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
contentContainer: {
},
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
InteractionManager,
Platform,
} from 'react-native';
import {getSlicedUrl} from '../../../classify/utils/Utils';
export default class List extends Component {
constructor(props) {
super(props);
this._renderHeader = this._renderHeader.bind(this);
this.state = {
width: Dimensions.get('window').width,
height: 0,
};
}
componentDidMount() {
let src = this.props.resource.src;
let bigPic = getSlicedUrl(src, 640, 640, 2);
Image.getSize(bigPic, (width, height) => {
let newWidth = Dimensions.get('window').width;
let newHeight = Dimensions.get('window').width/width*height;
this.setState({width: newWidth, height: newHeight });
});
}
_renderHeader() {
if (this.props.type == 'editor') {
return null;
}
return (
<TouchableOpacity onPress={()=>{this.props.onPressHeader&&this.props.onPressHeader(this.props.resource.author.url)}}>
<View style={styles.headerContainer}>
<Image style={styles.avatar} source={{uri:this.props.resource.author.avatar}}/>
<Text style={styles.name}>Sphinx</Text>
</View>
</TouchableOpacity>
)
}
render() {
let tagMap = {
'潮品' :require('../../image/chaopin_icon.png'),
'话题' :require('../../image/huati_icon.png'),
'潮人' :require('../../image/chaoren_icon.png'),
'搭配' :require('../../image/dapei_icon.png'),
'小贴士' :require('../../image/xiaotieshi_icon.png'),
'专题' :require('../../image/zuanti_icon.png')
};
let {
browse,
category_name,
intro,
isPraise,
praise_num,
publish_time,
src,
title,
views_num,
url,
share,
id,
} = this.props.resource;
let urlAry = url.split('&');
let shareParam = {
title,
'content': intro,
'image': src,
'url': urlAry[0],
};
let bigPic = getSlicedUrl(src, 640, 640, 2);
let tagImg = tagMap[category_name];
if (!tagImg) {
tagImg = "../../image/chaopin_icon.png";
}
let likeImg = isPraise == 'N'?require('../../image/wsc_icon.png'):require('../../image/sc_icon.png');
let isLike = isPraise == 'N';
return (
<TouchableOpacity activeOpacity={0.5} onPress={() => {
this.props.onPressCell&&this.props.onPressCell(url);
}}>
<View style={styles.container}>
<View style={styles.sapatorView}/>
{this._renderHeader()}
<Image style={styles.image,{width: this.state.width, height: this.state.height}} source={{uri:bigPic}}>
<Image style={styles.tagContainer} source={tagImg}>
<Text style={styles.tagText}>{category_name}</Text>
</Image>
</Image>
<Text style={styles.titleText}>{title}</Text>
<Text style={styles.contentText} numberOfLines={3}>{intro}</Text>
<View style={styles.toolContainer}>
<Image source={require('../../image/time_icon.png')} style={styles.iconThumb}resizeMode={'contain'}></Image>
<Text style={styles.text}>{publish_time}</Text>
<Image source={require('../../image/shared_view_icon.png')} style={styles.iconThumb}resizeMode={'contain'}></Image>
<Text style={styles.text}>{views_num}</Text>
<TouchableOpacity style={styles.likeButton} onPress={()=>{this.props.onPressLike&&this.props.onPressLike(id, isLike)}}>
<Image source={likeImg}/>
</TouchableOpacity>
<TouchableOpacity style={styles.shareButton} onPress={()=>{this.props.onPressShare&&this.props.onPressShare(shareParam)}}>
<Image source={require('../../image/shared_sharebuttom_highlighted.png')}/>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
contentContainer: {
backgroundColor: 'white',
},
sapatorView: {
backgroundColor: 'white',
width: width,
height: 16,
borderColor: 'rgb(215, 215, 215)',
borderTopWidth: 1,
borderBottomWidth: 1,
},
headerContainer: {
height: 60,
width: width,
backgroundColor: 'white',
flexDirection: 'row',
},
avatar: {
width: 30,
height: 30,
borderRadius: 15,
marginLeft: 17,
marginTop: 15,
},
name: {
fontSize: 17,
color: 'rgb(83, 83, 83)',
textAlign: 'left',
width: width - 80,
marginTop: 22,
marginLeft: 17,
},
image: {
width: width,
backgroundColor: 'gray',
height: width,
},
tagContainer: {
width: 88,
height: 25,
alignItems: 'center',
flexDirection: 'column',
},
tagText: {
backgroundColor: 'transparent',
color: 'white',
fontSize: 15,
textAlign: 'center',
paddingTop: 5,
paddingRight: 20,
},
titleText: {
backgroundColor: 'white',
color: 'black',
fontSize: 22,
marginLeft: 16,
width: width -32,
marginTop: 20,
},
contentText: {
backgroundColor: 'white',
color: 'gray',
fontSize: 14,
marginLeft: 16,
width: width - 32,
lineHeight: 25,
},
likeButton: {
position: 'absolute',
width: 17,
height: 16,
left: width - 100,
top:14,
},
shareButton: {
position: 'absolute',
width: 44,
height: 44,
left: width - 60,
top:0,
},
toolContainer: {
flexDirection: 'row',
height: 45,
width:width,
alignItems: 'center',
marginTop: 5,
},
iconThumb: {
marginLeft: 16,
height: 12,
width:20,
},
text: {
marginLeft: 5,
fontSize: 12,
color: 'gray'
},
});
... ...
import keyMirror from 'key-mirror';
export default keyMirror({
SET_PLATFORM: null,
SET_HOST: null,
SET_SERVICE_HOST: null,
SET_CHANNEL: null,
SET_ARTICLE_ID: null,
GET_ARTICLE_REQUEST: null,
GET_ARTICLE_SUCCESS: null,
GET_ARTICLE_FAILURE: null,
GET_AUTHOR_REQUEST: null,
GET_AUTHOR_SUCCESS: null,
GET_AUTHOR_FAILURE: null,
GET_ARTICLE_CONTENT_REQUEST: null,
GET_ARTICLE_CONTENT_SUCCESS: null,
GET_ARTICLE_CONTENT_FAILURE: null,
GET_BRAND_REQUEST: null,
GET_BRAND_SUCCESS: null,
GET_BRAND_FAILURE: null,
GET_OTHER_ARTICLE_REQUEST: null,
GET_OTHER_ARTICLE_SUCCESS: null,
GET_OTHER_ARTICLE_FAILURE: null,
GET_WEIXIN_PUBLIC_REQUEST: null,
GET_WEIXIN_PUBLIC_SUCCESS: null,
GET_WEIXIN_PUBLIC_FAILURE: null,
PRODUCT_BY_SKNS_REQUEST: null,
PRODUCT_BY_SKNS_SUCCESS: null,
PRODUCT_BY_SKNS_FAILURE: null,
SET_LIST_TYPE: null,
SET_AUTHOR_ID: null,
SET_TAG: null,
GET_ARTICLE_LIST_REQUEST: null,
GET_ARTICLE_LIST_SUCCESS: null,
GET_ARTICLE_LIST_FAILURE: null,
SET_GOOESGROUP_FILTER: null,
SET_GOOESGROUP_Y: null,
LIKE_ARTICLE: null,
});
... ...
'use strict'
import React, {Component} from 'react';
import ReactNative, {
StyleSheet,
Dimensions,
Platform,
View,
Text,
NativeModules,
InteractionManager,
NativeAppEventEmitter,
} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {Map} from 'immutable';
import * as detailActions from '../reducers/detail/detailActions';
import Detail from '../components/detail/Detail';
import {urlAddParamOfType} from '../../common/utils/urlHandler';
const actions = [
detailActions,
];
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 DetailContainer extends Component {
constructor(props) {
super(props);
this._onPressFilter = this._onPressFilter.bind(this);
this._onPressAuthor = this._onPressAuthor.bind(this);
this._onPressMoreLink = this._onPressMoreLink.bind(this);
this._onPressProduct = this._onPressProduct.bind(this);
this._onPressWeixin = this._onPressWeixin.bind(this);
this._onPressGoodY = this._onPressGoodY.bind(this);
this._onPressLink = this._onPressLink.bind(this);
this._onPressTag = this._onPressTag.bind(this);
this._onPressBrand = this._onPressBrand.bind(this);
this._onPressArticle = this._onPressArticle.bind(this);
}
componentDidMount() {
this.props.actions.getArticle();
this.props.actions.getArticleContent();
this.props.actions.getBrand();
this.props.actions.getWeixinPublic();
}
componentWillUnmount() {
}
_onPressFilter(value) {
this.props.actions.setGoodsGroupFilter(value);
}
_onPressGoodY(y) {
this.props.actions.setGoodsGroupY(y);
}
_onPressAuthor(url) {
let authorUrl = urlAddParamOfType(url, '11')
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(authorUrl);
}
_onPressBrand(url) {
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onPressArticle(url) {
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onPressLink(url) {
ReactNative.NativeModules.YH_CommonHelper.goLinkUrl(url);
}
_onPressTag(url) {
let taggedUrl = urlAddParamOfType(url, '12')
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(taggedUrl);
}
_onPressMoreLink(url) {
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onPressWeixin(data) {
ReactNative.NativeModules.YH_CommonHelper.copyWechatIdToClipboard(data);
}
_onPressProduct(product) {
let productSkn = product && product.get('product_skn', 0);
if (!productSkn) {
return;
}
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.productDetail","params":{"product_skn":"${productSkn}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
render() {
let {detail} = this.props;
return (
<View style={styles.container}>
<Detail
resource={detail}
onPressFilter= {this._onPressFilter}
onPressAuthor={this._onPressAuthor}
onPressMoreLink={this._onPressMoreLink}
onPressProduct={this._onPressProduct}
onPressWeixin={this._onPressWeixin}
onPressGoodY={this._onPressGoodY}
onPressLink={this._onPressLink}
onPressTag={this._onPressTag}
onPressBrand={this._onPressBrand}
onPressArticle={this._onPressArticle}
/>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps)(DetailContainer);
... ...
'use strict'
import React, {Component} from 'react';
import ReactNative, {
StyleSheet,
Dimensions,
Platform,
View,
Text,
Image,
NativeModules,
InteractionManager,
NativeAppEventEmitter,
} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {Map} from 'immutable';
import * as listActions from '../reducers/list/listActions';
import List from '../components/list/List';
import {urlAddParamOfType} from '../../common/utils/urlHandler';
const actions = [
listActions,
];
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 ListContainer extends Component {
constructor(props) {
super(props);
this._onRefresh = this._onRefresh.bind(this);
this._onEndReached = this._onEndReached.bind(this);
this._onPressCell = this._onPressCell.bind(this);
this._onPressShare = this._onPressShare.bind(this);
this._onPressLike = this._onPressLike.bind(this);
this._onPressHeader = this._onPressHeader.bind(this);
this.subscription = NativeAppEventEmitter.addListener(
'ChannelDidChangeEvent',
(reminder) => {
this.home && this.home.trigggePullToRefresh();
}
);
}
componentDidMount() {
this.props.actions.getArticleList();
}
componentWillUnmount() {
this.subscription && this.subscription.remove();
}
_onPressCell(url) {
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
_onPressHeader(url) {
let taggedUrl = urlAddParamOfType(url, '11')
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(taggedUrl);
}
_onRefresh() {
InteractionManager.runAfterInteractions(() => {
this.props.actions.getArticleList(true);
});
}
_onPressShare(param) {
ReactNative.NativeModules.YH_CommonHelper.shareWithParam(param);
}
_onPressLike(article_id, isLike) {
this.props.actions.onPressLike(article_id, isLike);
}
_onEndReached() {
InteractionManager.runAfterInteractions(() => {
this.props.actions.getArticleList();
});
}
render() {
let {list} = this.props;
console.log('aaaaa');
console.log(this.props);
return (
<View style={styles.container}>
<List
ref={(c) => {
this.sale = c;
}}
data={list}
onPressCell={this._onPressCell}
onEndReached={this._onEndReached}
onPressShare={this._onPressShare}
onPressLike={this._onPressLike}
onPressHeader={this._onPressHeader}
/>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps)(ListContainer);
... ...
'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
};
}
\ No newline at end of file
... ...
'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://service.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 DetailService from '../../services/DetailService';
import helper from '../../../common/utils/helper';
const {
SET_ARTICLE_ID,
GET_ARTICLE_REQUEST,
GET_ARTICLE_SUCCESS,
GET_ARTICLE_FAILURE,
GET_AUTHOR_REQUEST,
GET_AUTHOR_SUCCESS,
GET_AUTHOR_FAILURE,
GET_ARTICLE_CONTENT_REQUEST,
GET_ARTICLE_CONTENT_SUCCESS,
GET_ARTICLE_CONTENT_FAILURE,
GET_BRAND_REQUEST,
GET_BRAND_SUCCESS,
GET_BRAND_FAILURE,
GET_OTHER_ARTICLE_REQUEST,
GET_OTHER_ARTICLE_SUCCESS,
GET_OTHER_ARTICLE_FAILURE,
GET_WEIXIN_PUBLIC_REQUEST,
GET_WEIXIN_PUBLIC_SUCCESS,
GET_WEIXIN_PUBLIC_FAILURE,
PRODUCT_BY_SKNS_REQUEST,
PRODUCT_BY_SKNS_SUCCESS,
PRODUCT_BY_SKNS_FAILURE,
SET_GOOESGROUP_FILTER,
SET_GOOESGROUP_Y,
} = require('../../constants/actionTypes').default;
export function setArticleId(id) {
return {
type: SET_ARTICLE_ID,
payload: id,
};
}
function getArticleRequest() {
return {
type: GET_ARTICLE_REQUEST,
};
}
function getArticleSuccess(json) {
return {
type: GET_ARTICLE_SUCCESS,
payload: json
};
}
function getArticleFailure(error) {
return {
type: GET_ARTICLE_FAILURE,
payload: error
};
}
export function getArticle(reload = false) {
return (dispatch, getState) => {
let {app, detail} = getState();
let {articleId, article} = detail;
if (reload) {
// 强制刷新数据
} else {
if (article.isFetching) {
return;
}
}
dispatch(getArticleRequest());
return new DetailService(app.serviceHost).getArticle(articleId)
.then(json => {
dispatch(getArticleSuccess(json));
dispatch(getAuthor(json));
dispatch(getOtherArticle(json));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getArticleFailure(error));
});
};
}
function getAuthorRequest() {
return {
type: GET_AUTHOR_REQUEST,
};
}
function getAuthorSuccess(json) {
return {
type: GET_AUTHOR_SUCCESS,
payload: json
};
}
function getAuthorFailure(error) {
return {
type: GET_AUTHOR_FAILURE,
payload: error
};
}
export function getAuthor(article, reload = false) {
return (dispatch, getState) => {
let {app, detail} = getState();
let {author} = detail;
if (reload) {
// 强制刷新数据
} else {
if (author.isFetching) {
return;
}
}
let authorId = article.author_id;
if (!authorId) {
return;
}
dispatch(getAuthorRequest());
return new DetailService(app.serviceHost).getAuthor(authorId)
.then(json => {
let payload = json;
dispatch(getAuthorSuccess(json));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getAuthorFailure(error));
});
};
}
function getArticleContentRequest() {
return {
type: GET_ARTICLE_CONTENT_REQUEST,
};
}
function getArticleContentSuccess(json) {
return {
type: GET_ARTICLE_CONTENT_SUCCESS,
payload: json
};
}
function getArticleContentFailure(error) {
return {
type: GET_ARTICLE_CONTENT_FAILURE,
payload: error
};
}
export function getArticleContent(reload = false) {
return (dispatch, getState) => {
let {app, detail} = getState();
let {articleId, content} = detail;
if (reload) {
// 强制刷新数据
} else {
if (content.isFetching) {
return;
}
}
dispatch(getArticleContentRequest());
return new DetailService(app.serviceHost).getArticleContent(articleId)
.then(json => {
let payload = parseArticleContent(json);
payload.map((item, i) => {
if (item.template_name == 'goods') {
dispatch(goodsProductBySkns(item, i));
} else if (item.template_name == 'goods_group') {
dispatch(goodsGroupProductBySkns(item, i));
}
});
dispatch(getArticleContentSuccess(payload));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getArticleContentFailure(error));
});
};
}
function parseArticleContent(json) {
let contents = [];
let goodsSkns = [];
let groupGoodsSkns = [];
json && json.map((item, i) => {
for (let i in item) {
if (item.hasOwnProperty(i)) {
let data = item[i];
let templateName = data.template_name;
if (templateName == 'single_image') {
let innerData = data.data[0];
innerData.src = helper.image(innerData.src, 640, 640);
data.data = innerData;
} else if (templateName == 'small_pic') {
let innerData = data.data[0];
let src0 = helper.image(data.data[0].src, 315, 420);
let src1 = helper.image(data.data[1].src, 315, 420);
data.data = [
{src: src0},
{src: src1},
];
} else if (templateName == 'link') {
data.data = {
url: data.data[0].url,
};
} else if (templateName == 'goods_group') {
} else if (templateName == 'goods') {
} else if (templateName == 'text') {
}
contents.push(data);
};
}
});
return contents;
}
/**
* [获取商品的ICON]
* @param {[int]} type [类型]
* @return {[type]}
*/
function getProductIcon(type) {
const icons = {
1: 'cloth',
3: 'pants',
4: 'dress',
6: 'shoe',
7: 'bag',
10: 'lamp',
241: 'headset',
8: 'watch',
360: 'swim-suit',
308: 'under'
};
return icons[type] || '';
}
function productBySknsRequest() {
return {
type: PRODUCT_BY_SKNS_REQUEST,
};
}
function productBySknsSuccess(json) {
return {
type: PRODUCT_BY_SKNS_SUCCESS,
payload: json
};
}
function productBySknsFailure(error) {
return {
type: PRODUCT_BY_SKNS_FAILURE,
payload: error
};
}
export function goodsProductBySkns(item, contentIndex) {
return (dispatch, getState) => {
let skns = [];
let productImages = {};
// 遍历取得SKN
item.data.map((item2) => {
skns.push(item2.id);
productImages[item2.id] = item2.src;
});
if (!skns) {
return;
}
let {app, detail} = getState();
dispatch(productBySknsRequest());
return new DetailService(app.host).productInfoBySkns(skns)
.then(json => {
let productList = [];
// 最多显示4个
let result = json.product_list.slice(0, 4);
result.map((item3, i3) => {
let src = productImages[item3.product_skn];
if (src) {
if (item3.tags.includes('is_soon_sold_out')) {
item3.tags = ['is_soon_sold_out'];
} else {
item3.tags = [];
}
item3.default_images = helper.image(src, 235, 314);
productList.push(item3);
}
});
dispatch(productBySknsSuccess({
productList,
contentIndex,
}));
})
.catch(error => {
dispatch(productBySknsFailure(error));
});
};
}
export function goodsGroupProductBySkns(item, contentIndex) {
return (dispatch, getState) => {
let skns = [];
let productImages = {};
// 遍历取得SKN
item.data.map((item2) => {
item2.list.map((item3) => {
skns.push(item3.id);
productImages[item3.id] = item3.src;
});
});
if (!skns) {
return;
}
let {app, detail} = getState();
dispatch(productBySknsRequest());
return new DetailService(app.host).productInfoBySkns(skns)
.then(json => {
let result = json.product_list;
result.map((item4) => {
let src = productImages[item4.product_skn];
if (src) {
if (item4.tags.includes('is_soon_sold_out')) {
item4.tags = ['is_soon_sold_out'];
} else {
item4.tags = [];
}
item4.default_images = helper.image(src, 235, 314);
}
});
let productList = []
item.data.map((item5) => {
let good = {
thumb: item5.cover ? helper.image(item5.cover.cover, 235, 314) : '',
type: item5.cover ? getProductIcon(item5.cover.maxSortId) : '',
list: [],
};
result.map((item6) => {
let sameSkn = item5.list.filter((item7) => {
return item6.product_skn == item7.id;
});
if (sameSkn && sameSkn.length > 0) {
good.list.push(item6);
}
});
productList.push(good);
});
dispatch(productBySknsSuccess({
productList,
contentIndex,
}));
})
.catch(error => {
dispatch(productBySknsFailure(error));
});
};
}
function getBrandRequest() {
return {
type: GET_BRAND_REQUEST,
};
}
function getBrandSuccess(json) {
return {
type: GET_BRAND_SUCCESS,
payload: json
};
}
function getBrandFailure(error) {
return {
type: GET_BRAND_FAILURE,
payload: error
};
}
export function getBrand(reload = false) {
return (dispatch, getState) => {
let {app, detail} = getState();
let {articleId, brand} = detail;
if (reload) {
// 强制刷新数据
} else {
if (brand.isFetching) {
return;
}
}
dispatch(getBrandRequest());
return new DetailService(app.serviceHost).getBrand(articleId)
.then(json => {
dispatch(getBrandSuccess(json));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getBrandFailure(error));
});
};
}
function getOtherArticleRequest() {
return {
type: GET_OTHER_ARTICLE_REQUEST,
};
}
function getOtherArticleSuccess(json) {
return {
type: GET_OTHER_ARTICLE_SUCCESS,
payload: json
};
}
function getOtherArticleFailure(error) {
return {
type: GET_OTHER_ARTICLE_FAILURE,
payload: error
};
}
export function getOtherArticle(article, reload = false) {
return (dispatch, getState) => {
let {app, detail} = getState();
let {articleId, otherArticle} = detail;
if (reload) {
// 强制刷新数据
} else {
if (otherArticle.isFetching) {
return;
}
}
if (typeof article.tag === 'undefined') {
return;
}
let tag = article.tag;
let offset = 0;
let limit = 3;
dispatch(getOtherArticleRequest());
return new DetailService(app.serviceHost).getOtherArticle(articleId, tag, offset, limit)
.then(json => {
let payload = parseOtherArticle(json);
dispatch(getOtherArticleSuccess(payload));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getOtherArticleFailure(error));
});
};
}
function parseOtherArticle(json) {
json && json.map((item, i) => {
item.thumb = helper.image(item.thumb, 279, 175);
});
return json;
}
function getWeixinPublicRequest() {
return {
type: GET_WEIXIN_PUBLIC_REQUEST,
};
}
function getWeixinPublicSuccess(json) {
return {
type: GET_WEIXIN_PUBLIC_SUCCESS,
payload: json
};
}
function getWeixinPublicFailure(error) {
return {
type: GET_WEIXIN_PUBLIC_FAILURE,
payload: error
};
}
export function getWeixinPublic() {
return (dispatch, getState) => {
let {app, detail} = getState();
let {articleId, weixin} = detail;
if (weixin.isFetching) {
return;
}
dispatch(getWeixinPublicRequest());
return new DetailService(app.host).getWeixinPublic()
.then(json => {
dispatch(getWeixinPublicSuccess(json));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getWeixinPublicFailure(error));
});
};
}
export function setGoodsGroupFilter(filter) {
return {
type: SET_GOOESGROUP_FILTER,
payload: filter
};
}
export function setGoodsGroupY(y) {
return {
type: SET_GOOESGROUP_Y,
payload: y
};
}
... ...
'use strict';
import {Record, List, Map} from 'immutable';
let InitialState = Record({
articleId: '60675',
article: 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({
isFetching: false,
error: null,
data: List(),
})),
goods_group_Filter: 0,
goods_group_y: 0,
});
export default InitialState;
... ...
'use strict';
import InitialState from './detailInitialState';
import Immutable, {Map} from 'immutable';
const {
SET_ARTICLE_ID,
GET_ARTICLE_REQUEST,
GET_ARTICLE_SUCCESS,
GET_ARTICLE_FAILURE,
GET_AUTHOR_REQUEST,
GET_AUTHOR_SUCCESS,
GET_AUTHOR_FAILURE,
GET_ARTICLE_CONTENT_REQUEST,
GET_ARTICLE_CONTENT_SUCCESS,
GET_ARTICLE_CONTENT_FAILURE,
GET_BRAND_REQUEST,
GET_BRAND_SUCCESS,
GET_BRAND_FAILURE,
GET_OTHER_ARTICLE_REQUEST,
GET_OTHER_ARTICLE_SUCCESS,
GET_OTHER_ARTICLE_FAILURE,
GET_WEIXIN_PUBLIC_REQUEST,
GET_WEIXIN_PUBLIC_SUCCESS,
GET_WEIXIN_PUBLIC_FAILURE,
PRODUCT_BY_SKNS_SUCCESS,
SET_GOOESGROUP_FILTER,
SET_GOOESGROUP_Y,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function detailReducer(state=initialState, action) {
switch(action.type) {
case SET_ARTICLE_ID: {
return state.set('articleId', action.payload);
}
case GET_ARTICLE_REQUEST: {
return state.setIn(['article', 'isFetching'], true)
.setIn(['article', 'error'], null);
}
case GET_ARTICLE_SUCCESS: {
return state.setIn(['article', 'isFetching'], false)
.setIn(['article', 'error'], null)
.setIn(['article', 'data'], Immutable.fromJS(action.payload));
}
case GET_ARTICLE_FAILURE: {
return state.setIn(['article', 'isFetching'], false)
.setIn(['article', 'error'], action.payload);
}
case GET_AUTHOR_REQUEST: {
return state.setIn(['author', 'isFetching'], true)
.setIn(['author', 'error'], null);
}
case GET_AUTHOR_SUCCESS: {
return state.setIn(['author', 'isFetching'], false)
.setIn(['author', 'error'], null)
.setIn(['author', 'data'], Immutable.fromJS(action.payload));
}
case GET_AUTHOR_FAILURE: {
return state.setIn(['author', 'isFetching'], false)
.setIn(['author', 'error'], action.payload);
}
case GET_ARTICLE_CONTENT_REQUEST: {
return state.setIn(['content', 'isFetching'], true)
.setIn(['content', 'error'], null);
}
case GET_ARTICLE_CONTENT_SUCCESS: {
return state.setIn(['content', 'isFetching'], false)
.setIn(['content', 'error'], null)
.setIn(['content', 'data'], Immutable.fromJS(action.payload));
}
case GET_ARTICLE_CONTENT_FAILURE: {
return state.setIn(['content', 'isFetching'], false)
.setIn(['content', 'error'], action.payload);
}
case GET_BRAND_REQUEST: {
return state.setIn(['brand', 'isFetching'], true)
.setIn(['brand', 'error'], null);
}
case GET_BRAND_SUCCESS: {
return state.setIn(['brand', 'isFetching'], false)
.setIn(['brand', 'error'], null)
.setIn(['brand', 'data'], Immutable.fromJS(action.payload));
}
case GET_BRAND_FAILURE: {
return state.setIn(['brand', 'isFetching'], false)
.setIn(['brand', 'error'], action.payload);
}
case GET_OTHER_ARTICLE_REQUEST: {
return state.setIn(['otherArticle', 'isFetching'], true)
.setIn(['otherArticle', 'error'], null);
}
case GET_OTHER_ARTICLE_SUCCESS: {
return state.setIn(['otherArticle', 'isFetching'], false)
.setIn(['otherArticle', 'error'], null)
.setIn(['otherArticle', 'data'], Immutable.fromJS(action.payload));
}
case GET_OTHER_ARTICLE_FAILURE: {
return state.setIn(['otherArticle', 'isFetching'], false)
.setIn(['otherArticle', 'error'], action.payload);
}
case GET_WEIXIN_PUBLIC_REQUEST: {
return state.setIn(['weixin', 'isFetching'], true)
.setIn(['weixin', 'error'], null);
}
case GET_WEIXIN_PUBLIC_SUCCESS: {
return state.setIn(['weixin', 'isFetching'], false)
.setIn(['weixin', 'error'], null)
.setIn(['weixin', 'data'], Immutable.fromJS(action.payload));
}
case GET_WEIXIN_PUBLIC_FAILURE: {
return state.setIn(['weixin', 'isFetching'], false)
.setIn(['weixin', 'error'], action.payload);
}
case PRODUCT_BY_SKNS_SUCCESS: {
let {
productList,
contentIndex
} = action.payload;
return state.setIn(['content', 'data', contentIndex, 'productList'], Immutable.fromJS(productList));
}
case SET_GOOESGROUP_FILTER: {
return state.set('goods_group_Filter', action.payload);
}
case SET_GOOESGROUP_Y: {
return state.set('goods_group_y', action.payload);
}
}
return state;
}
... ...
import {combineReducers} from 'redux';
import app from './app/appReducer';
import list from './list/listReducer';
import detail from './detail/detailReducer';
const rootReducer = combineReducers({
app,
list,
detail,
});
export default rootReducer;
... ...
'use strict';
import ReactNative from 'react-native';
import ListService from '../../services/ListService';
const {
SET_LIST_TYPE,
SET_AUTHOR_ID,
SET_TAG,
GET_ARTICLE_LIST_REQUEST,
GET_ARTICLE_LIST_SUCCESS,
GET_ARTICLE_LIST_FAILURE,
LIKE_ARTICLE,
} = require('../../constants/actionTypes').default;
export function setListType(type) {
return {
type: SET_LIST_TYPE,
payload: type,
};
}
export function setAuthorId(id) {
return {
type: SET_AUTHOR_ID,
payload: id,
};
}
export function setTag(tag) {
return {
type: SET_TAG,
payload: tag,
};
}
function getArticleListRequest(reload) {
return {
type: GET_ARTICLE_LIST_REQUEST,
payload: reload,
};
}
function getArticleListSuccess(json) {
return {
type: GET_ARTICLE_LIST_SUCCESS,
payload: json
};
}
function getArticleListFailure(error) {
return {
type: GET_ARTICLE_LIST_FAILURE,
payload: error
};
}
function likeArticle(json) {
return {
type: LIKE_ARTICLE,
payload: json,
}
}
export function onPressLike(article_id, isLike) {
return (dispatch, getState) => {
let {app, list} = getState();
let likeRequest = (article_id, uid, isLike) => {
dispatch(likeArticle({article_id, isLike}));
if (isLike) {
return new ListService(app.serviceHost).setArticleFavorite(article_id, uid)
}else {
return new ListService(app.serviceHost).cancleArticleFavorite(article_id, uid)
}
}
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
likeRequest(article_id, uid, isLike);
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
likeRequest(article_id, uid, isLike);
})
.catch(error => {
});
});
};
}
export function getArticleList(reload = false) {
return (dispatch, getState) => {
let {app, list} = getState();
let {type, authorId, tag, articles} = list;
if (reload) {
} else {
if (articles.isFetching || articles.endReached || articles.error) {
return;
}
}
let fetchList = (authorId, tag, gender, uid, page, pageSize) => {
dispatch(getArticleListRequest(reload));
return new ListService(app.serviceHost).getArticleList(authorId, tag, gender, uid, page, pageSize)
.then(json => {
let payload = parseArticleList(json);
payload.endReached = payload.currentPage == payload.pageCount;
if (payload.currentPage > 1) {
let oldList = articles.list.toJS();
let list = [...oldList, ...payload.list];
payload.list = list;
}
dispatch(getArticleListSuccess(payload));
// dispatch(dataExposure(payload.logFloors));
})
.catch(error => {
dispatch(getArticleListFailure(error));
});
};
let gender = '';
let sortId = 0;
let uid = 0;
let page = articles.currentPage + 1;
let pageSize = articles.pageSize;
Promise.all([
ReactNative.NativeModules.YH_CommonHelper.currentGender(),
ReactNative.NativeModules.RNNativeConfig.uid(),
]).then(result => {
gender = result[0];
uid = result[1];
fetchList(authorId, tag, gender, uid, page, pageSize);
}).catch(error => {
});
};
}
function parseArticleList(json) {
let currentPage = json && json.page ? json.page : 1;
let pageCount = json && json.totalPage ? json.totalPage : 0;
let total = json && json.total ? json.total : 0;
let list = json && json.list && json.list.artList ? json.list.artList : [];
list && list.map((item) => {
});
return {
list,
currentPage,
pageCount,
total,
};
}
... ...
'use strict';
import {Record, List, Map} from 'immutable';
let InitialState = Record({
type: 'editor', // editor or tag
authorId: '',
tag: '',
author: new (Record({
isFetching: false,
error: null,
data: null,
})),
articles: new (Record({
ptr: false,
isFetching: false,
error: null,
list: null,
currentPage: 0,
pageCount: 0,
pageSize: 60,//60,
total: 0,
endReached: false,
})),
});
export default InitialState;
... ...
'use strict';
import InitialState from './listInitialState';
import Immutable, {Map} from 'immutable';
const {
SET_LIST_TYPE,
SET_AUTHOR_ID,
SET_TAG,
GET_ARTICLE_LIST_REQUEST,
GET_ARTICLE_LIST_SUCCESS,
GET_ARTICLE_LIST_FAILURE,
LIKE_ARTICLE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function listReducer(state=initialState, action) {
switch(action.type) {
case SET_LIST_TYPE: {
return state.set('type', action.payload);
}
case SET_AUTHOR_ID: {
return state.set('authorId', action.payload);
}
case SET_TAG: {
return state.set('tag', action.payload);
}
case GET_ARTICLE_LIST_REQUEST: {
return state.setIn(['articles', 'isFetching'], true)
.setIn(['articles', 'error'], null)
.setIn(['articles', 'ptr'], action.payload);
}
case GET_ARTICLE_LIST_SUCCESS: {
let {
list,
currentPage,
pageCount,
total,
endReached,
} = action.payload;
let newState = state.setIn(['articles', 'isFetching'], false)
.setIn(['articles', 'error'], null)
.setIn(['articles', 'list'], Immutable.fromJS(list))
.setIn(['articles', 'currentPage'], currentPage)
.setIn(['articles', 'pageCount'], pageCount)
.setIn(['articles', 'total'], total)
.setIn(['articles', 'endReached'], endReached);
return newState;
}
case LIKE_ARTICLE: {
let {
article_id,
isLike,
} = action.payload;
let originAry = state.get('articles').get('list').toJS();
for (var i = 0; i < originAry.length; i++) {
if (originAry[i].id == article_id) {
console.log('......find....');
originAry[i].isPraise = isLike?'Y':'N';
}
}
let newState = state.setIn(['articles', 'list'], Immutable.fromJS(originAry));
return newState;
}
break;
case GET_ARTICLE_LIST_FAILURE: {
return state.setIn(['articles', 'isFetching'], false)
.setIn(['articles', 'error'], action.payload)
}
}
return state;
}
... ...
'use strict';
import Request from '../../common/services/Request';
export default class DetailService {
constructor (host) {
let baseURL = 'http://service.yoho.cn';
if(host){
baseURL = host;
}
this.api = new Request(baseURL);
}
// 获取资讯
async getArticle(article_id) {
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',
body: {
article_id,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
// 获取资讯相关的其它资讯
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 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);
});
}
async productInfoBySkns(skns) {
return await this.api.get({
url: '',
body: {
method: 'app.search.li',
query: skns.join(' '),
limit: skns.length,
order: 's_t_desc',
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
}
... ...
'use strict';
import Request from '../../common/services/Request';
export default class ListService {
constructor (host) {
let baseURL = 'http://api.yoho.cn';
if(host){
baseURL = host;
}
this.api = new Request(baseURL);
}
// 获取资讯
async getArticleList(author_id, tag, gender='1,3', uid=0, page=1, limit=20, sort_id=0) {
console.log(uid);
return await this.api.get({
url: '/guang/api/v2/article/getList',
body: {
gender,
sort_id,
uid,
tag,
author_id,
page,
limit,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async setArticleFavorite(article_id, uid=0) {
return await this.api.get({
url: '/guang/api/v1/favorite/setFavorite',
body: {
article_id,
uid,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async cancleArticleFavorite(article_id, uid=0) {
return await this.api.get({
url: '/guang/api/v1/favorite/cancelFavorite',
body: {
article_id,
uid,
}
})
.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);
};
... ...
... ... @@ -19,8 +19,10 @@ import {Record, List, Map} from 'immutable';
import appInitialState from './reducers/app/appInitialState';
import plustarInitialState from './reducers/plustar/plustarInitialState';
import detailInitialState from './reducers/detail/detailInitialState';
import PlustarContainer from './containers/PlustarContainer';
import DetailContainer from './containers/DetailContainer';
import {
setPlatform,
... ... @@ -32,10 +34,16 @@ import {
setGender,
} from './reducers/plustar/plustarActions';
import {
setBrandId,
setId,
} from './reducers/detail/detailActions';
function getInitialState() {
const _initState = {
app: (new appInitialState()),
plustar: (new plustarInitialState()),
detail: (new detailInitialState()),
};
return _initState;
}
... ... @@ -78,19 +86,31 @@ export default function native(platform) {
render() {
const store = configureStore(getInitialState());
store.dispatch(setPlatform(platform));
let segment = getInitSegment(this.props.initType);
store.dispatch(setSegment(segment));
if (this.props.initType == 0) {
let gender = this.props.genderType;
store.dispatch(setGender(gender));
let type = this.props.type;
if (type == 'detail') {
let id = this.props.id;
store.dispatch(setId(id));
return (
<Provider store={store}>
<DetailContainer />
</Provider>
);
} else {
let segment = getInitSegment(this.props.initType);
store.dispatch(setSegment(segment));
if (this.props.initType == 0) {
let gender = this.props.genderType;
store.dispatch(setGender(gender));
}
return (
<Provider store={store}>
<PlustarContainer />
</Provider>
);
}
return (
<Provider store={store}>
<PlustarContainer />
</Provider>
);
}
});
... ...
/*
* 明星原创--资讯组件
* create by 陈林 2016.12.13
*/
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
TouchableOpacity,
Dimensions,
NativeAppEventEmitter,
StyleSheet,
} from 'react-native';
import Immutable from 'immutable';
import SingleImage from './SingleImage';
import {SlicedImage} from '../../../common/components/SlicedImage';
export default class BrandArticleCell extends Component {
constructor(props) {
super(props);
this._renderTimeAndVisit = this._renderTimeAndVisit.bind(this);
}
shouldComponentUpdate(nextProps,nextState){
if (Immutable.is(nextProps.rowData, this.props.rowData)) {
return false;
} else {
return true;
}
}
//时间和访问次数以及是否喜欢
_renderTimeAndVisit(time, visit, likeNum, id, rowID, isliked){
let likeicon = isliked ? require("../../images/like_on.png") : require("../../images/like_off.png");
let likestyle = isliked ? styles.darkgrayfont : styles.grayfont;
return (
<View style={styles.timebar}>
<Image style={styles.timeicon} source={require("../../images/time_icon.png")} />
<Text style={styles.grayfont} numberOfLines={1}>{time}&nbsp;&nbsp;&nbsp;&nbsp;</Text>
<Image style={styles.eyeicon} source={require("../../images/eye_icon.png")} />
<Text style={styles.grayfont} numberOfLines={1}>{visit}&nbsp;&nbsp;&nbsp;&nbsp;</Text>
<TouchableOpacity
style={styles.likecontainer}
activeOpacity={1}
onPress={()=>{
this.props.onPressArticleLike && this.props.onPressArticleLike(id, rowID, !isliked, likeNum)
}}>
<Image style={styles.likeicon} source={likeicon} />
<Text style={likestyle} numberOfLines={1}>{likeNum}</Text>
</TouchableOpacity>
</View>
);
}
render() {
let {rowData, rowID} = this.props;
//url跳转地址
let url = rowData.get('url');
//id
let id = rowData.get('id');
//标题
let title = rowData.get('title');
//图片
let imgUrl = rowData.get('src').replace('{mode}', 2).replace('{width}', width).replace('{height}', width);
// let imgUrl = SlicedImage.getSlicedUrl(rowData.get('src'), width, width, 2);
//介绍
let intro = rowData.get('intro');
//发布时间
let publishTime = rowData.get('publish_time');
//访问次数
let viewsNum = rowData.get('views_num');
//喜欢信息
let likedata = rowData.get('like');
//是否喜欢
let isLiked = likedata ? likedata.get("isLiked") : false;
//喜欢人数
let likeCount = likedata ? likedata.get("count") : "0";
return (
<View style={styles.cellContainer}>
<TouchableOpacity
style={[styles.touchableContainer]}
activeOpacity={1}
onPress={()=>{
this.props.onPressArticle && this.props.onPressArticle(url)
}}>
<SingleImage source={imgUrl} />
<Text style={styles.title}>{title}</Text>
</TouchableOpacity>
<Text style={styles.content} numberOfLines={4}>{intro}</Text>
{this._renderTimeAndVisit(publishTime, viewsNum, likeCount, id, rowID, isLiked)}
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
cellContainer: {
width: width,
backgroundColor: '#ffffff',
},
touchableContainer: {
width: width,
},
// image: {
// width: width,
// height: Math.ceil(width * 410 / 655),
// },
title:{
width: width,
fontSize: 20,
fontWeight: 'bold',
paddingLeft: 15,
paddingRight: 15,
paddingTop:10,
paddingBottom:5,
},
content:{
width: width,
fontSize: 15,
color: '#3E3A39',
paddingLeft: 15,
paddingRight: 15,
paddingBottom:5,
},
timebar:{
width: width,
height: 30,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
paddingLeft: 15,
paddingRight: 15,
paddingBottom:5,
},
likecontainer:{
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
},
grayfont:{
fontSize: 15,
color: '#A5A5A5',
marginLeft: 5,
},
darkgrayfont:{
fontSize: 15,
color: '#444444',
marginLeft: 5,
},
timeicon:{
width: 12,
height:12,
},
eyeicon:{
width: 16,
height:12,
marginLeft: 6,
},
likeicon:{
width: 18,
height:17,
marginTop: 1,
},
});
... ...
/*
* 明星原创--资讯列表
* create by 陈林 2016.12.14
*/
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
} from 'react-native';
import BrandArticleCell from './BrandArticleCell';
export default class BrandArticleList extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderSeparator = this._renderSeparator.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
_renderHeader(){
return(
<View style={styles.headerContainer}>
<Text style={styles.headerText}>相关资讯</Text>
</View>
);
}
_renderRow(rowData, sectionID, rowID, highlightRow) {
return (
<BrandArticleCell
key={'row' + rowID}
rowID={rowID}
rowData={rowData}
onPressArticle={this.props.onPressArticle}
onPressArticleLike={this.props.onPressArticleLike}
/>
);
}
_renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
//最后一条记录无需Separator
if (!this.props || !this.props.articleList || (this.props.articleList.length - rowID) == 1) {
return null;
}
return (
<View key={'sep' + rowID} style={styles.separator}></View>
);
}
render() {
let {articleList} = this.props;
if (!articleList || articleList.length == 0) {
return null;
}
return (
<View style={styles.container}>
<ListView
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(articleList)}
renderRow={this._renderRow}
enableEmptySections = {true}
renderSeparator={this._renderSeparator}
onPressArticle={this.props.onPressArticle}
onPressArticleLike={this.props.onPressArticleLike}
renderHeader={this._renderHeader}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 320;
let styles = StyleSheet.create({
container: {
flex: 1,
},
contentContainer: {
flexWrap: 'wrap',
},
separator: {
width,
height: 20,
backgroundColor: '#e0e0e0',
},
headerContainer:{
height: 36 * DEVICE_WIDTH_RATIO,
marginLeft: 30 * DEVICE_WIDTH_RATIO,
marginRight: 30 * DEVICE_WIDTH_RATIO,
justifyContent: 'center',
alignItems: 'center',
borderColor: '#e0e0e0',
borderTopWidth: 0.5,
borderLeftWidth: 0.5,
borderRightWidth: 0.5,
borderBottomWidth: 0,
backgroundColor: "#ffffff",
},
headerText:{
// width: 'wrap',
// height: 'wrap',
lineHeight: 18,
fontSize: 16,
color: '#b0b0b0',
justifyContent: 'center',
alignItems: 'center',
},
});
... ...
/*
* 潮流优选、明星原创
*/
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
ScrollView,
Text,
NativeAppEventEmitter,
StyleSheet,
Image,
Dimensions,
TouchableOpacity,
} from 'react-native';
import Immutable, {Map} from 'immutable';
import SlicedImage from '../../../common/components/SlicedImage';
export default class BrandIntro extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
}
render() {
let{brandIntro, brandFav}=this.props;
let detail = brandIntro.get('detail').toJS();
let favIcon = brandFav.get('isFav') ? require("../../images/red_heart.png") : require("../../images/gray_heart.png");
let titleFoldArrowIcon = brandIntro.titleUnfold ? require("../../images/arrow_small_up.png") : require("../../images/arrow_small_down.png");
return (
<View style={styles.container}>
<SlicedImage style={[styles.coverImage, {width, height:coverImageHeight}]} source={{uri: detail.cover_img}}/>
<View style={styles.titleLikeView}>
<TouchableOpacity onPress={() => {
this.props.onPressFav && this.props.onPressFav(!brandFav.get('isFav'));
}}>
<Image style={styles.like} source={favIcon} />
</TouchableOpacity>
<Text style={styles.title}>
{detail.brand_name}
</Text>
</View>
<Text style={styles.intro} numberOfLines={brandIntro.titleUnfold ? 0 : 4}>
{detail.brand_intro}
</Text>
<TouchableOpacity onPress={() => {
this.props.onPressBrandIntroMore && this.props.onPressBrandIntroMore(!brandIntro.titleUnfold);
}}>
<View style={styles.moreView}>
<Image style={styles.moreArrow} source={titleFoldArrowIcon}/>
<Text style={styles.moreText}>
{brandIntro.titleUnfold ? '收起' : 'more'}
</Text>
</View>
</TouchableOpacity>
<View style={styles.brandIcon}>
<SlicedImage resizeMode={'stretch'} source={{uri: detail.brand_ico}} style={{width:brandIconWidth-2, height:brandIconWidth-2}}/>
</View>
<View style={styles.blackView}>
</View>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 320;
let coverImageHeight = 155 * DEVICE_WIDTH_RATIO;
let brandIconWidth = 84 * DEVICE_WIDTH_RATIO;
let styles = StyleSheet.create({
container: {
backgroundColor: 'white',
},
coverImage: {
height: 200,
},
brandIcon: {
position: 'absolute',
top: 116 * DEVICE_WIDTH_RATIO,
left: 25 * DEVICE_WIDTH_RATIO,
height: brandIconWidth,
width: brandIconWidth,
borderWidth: 1,
borderColor: '#b5b5b5',
},
titleLikeView: {
height:41 * DEVICE_WIDTH_RATIO,
flexDirection: 'row-reverse',
},
title: {
fontSize: 17 * DEVICE_WIDTH_RATIO,
width: 147.5 * DEVICE_WIDTH_RATIO,
height: 20.5 * DEVICE_WIDTH_RATIO,
top: 12 * DEVICE_WIDTH_RATIO,
},
like: {
width: 17 * DEVICE_WIDTH_RATIO,
height: 16 * DEVICE_WIDTH_RATIO,
margin: 15 * DEVICE_WIDTH_RATIO,
},
intro: {
margin: 15 * DEVICE_WIDTH_RATIO,
fontSize: 12 * DEVICE_WIDTH_RATIO,
color: '#444444',
lineHeight: Math.ceil(18 * DEVICE_WIDTH_RATIO),
},
moreView: {
paddingBottom: 15 * DEVICE_WIDTH_RATIO,
flexDirection: 'row-reverse',
},
moreText: {
fontSize: 14 * DEVICE_WIDTH_RATIO,
color: '#bbbbbb',
},
moreArrow: {
marginTop: 5 * DEVICE_WIDTH_RATIO,
marginLeft: 15 * DEVICE_WIDTH_RATIO,
marginRight: 15 * DEVICE_WIDTH_RATIO,
},
blackView: {
backgroundColor: '#f0f0f0',
height: 15 * DEVICE_WIDTH_RATIO,
}
});
... ...
'use strict'
import React, {Component} from 'react';
import ReactNative, {
StyleSheet,
Dimensions,
Platform,
View,
NativeModules,
InteractionManager,
NativeAppEventEmitter,
ListView,
} from 'react-native'
import {Map} from 'immutable';
import BrandIntro from './BrandIntro'
import BrandArticleList from './BrandArticleList'
import BrandArticleCell from './BrandArticleCell'
import NewArrival from './NewArrival'
import Prompt from '../../../coupon/components/coupon/Prompt';
export default class Detail extends React.Component{
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
}
componentWillReceiveProps(nextProps) {
if (this.props.detail.get('brandInfo').get('titleUnfold') && !nextProps.detail.get('brandInfo').get('titleUnfold')) {
this.listView.scrollTo({x: 0, y: 0, animated: false, });
}
}
renderRow(rowData, sectionID, rowID, highlightRow) {
if (!rowData && rowData.length == 0) {
return null;
}
switch (rowID) {
case 'brandInfo':
return(
<BrandIntro
brandIntro={rowData}
brandFav={this.props.detail.get('brandFav')}
onPressFav={this.props.onPressFav}
onPressBrandIntroMore={this.props.onPressBrandIntroMore}
addCanelFavTipRemove={this.props.addCanelFavTipRemove}
/>
);
case 'productList':
console.log(rowData);
console.log('rowData');
return (
<NewArrival
prodcutList={rowData}
onPressMoreProducts={this.props.onPressMoreProducts}
onPressProduct={this.props.onPressProduct}
moreProductUrl={this.props.detail.get('moreProductUrl')}
/>
);
case 'articleList':
return(
<BrandArticleList
articleList={rowData}
onPressArticle={this.props.onPressArticle}
onPressArticleLike={this.props.onPressArticleLike} />
);
default:
return null;
}
}
render() {
let {detail} = this.props;
let dataSource = {
brandInfo: detail.get('brandInfo'),
productList: detail.get('productList').toArray(),
articleList: detail.get('articleList').toArray(),
};
return (
<View style={styles.container}>
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
enableEmptySections={true}
dataSource={this.dataSource.cloneWithRows(dataSource)}
renderRow={this.renderRow}
/>
{detail.get('brandInfo').get('addCancelTip') !== '' ? <Prompt
text={detail.get('brandInfo').get('addCancelTip')}
duration={800}
onPromptHidden={this.props.addCanelFavTipRemove}
/> : null}
</View>
);
}
};
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
contentContainer:{
flexDirection: 'column',
flexWrap: 'wrap',
},
});
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
const {
Image,
View,
StyleSheet,
Dimensions,
Text,
ListView,
TouchableOpacity,
Platform,
} = ReactNative;
import Immutable, {Map} from 'immutable';
import BrandProductListCell from '../../../common/components/ListCell/ProductListCell';
import DeviceInfo from 'react-native-device-info';
export default class newArrive extends React.Component {
constructor(props) {
super (props);
this.renderRow = this.renderRow.bind(this);
this.renderHeader = this.renderHeader.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
// shouldComponentUpdate(nextProps){
// if (Immutable.is(nextProps.resource, this.props.resource)) {
// return false;
// } else {
// return true;
// }
// }
renderRow(rowData,sectionID,rowID,highlightRow) {
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}
/>
);
}
renderHeader() {
let fontFamilyStyle = {};
if (Platform.OS === 'ios') {
let systemVersion = DeviceInfo.getSystemVersion();
systemVersion = parseFloat(systemVersion);
if (systemVersion >= 9.0) {
fontFamilyStyle = {fontFamily: 'PingFang SC'};
}
}
return(
<View style={styles.container}>
<View style={{
width: width,
height: 0.5,
backgroundColor: '#e5e5e5',
}}/>
<View style={styles.title}>
<Text style={styles.text}>NEW ARRIVAL</Text>
<TouchableOpacity activeOpacity={0.5} style={styles.thumbnail} onPress={() => {
this.props.onPressMoreProducts && this.props.onPressMoreProducts(this.props.moreProductUrl);
}}>
<Image
source={require('../../images/btn_more_p.png')}
style={{width: 22, height: 4,backgroundColor:'white',marginTop:10}}
resizeMode={'contain'}
/>
</TouchableOpacity>
</View>
</View>
);
}
render() {
let prodcutList = this.props.prodcutList;
if (!prodcutList || prodcutList.length == 0) {
return null;
}
let backgroundWidth = width;
let backgroundHeight = 64 + 20 + Math.ceil(prodcutList.length / 2) * (rowHeight + rowMarginHorizontal);
return (
<View style={{
width: backgroundWidth,
height: backgroundHeight,
backgroundColor: 'white',
}}>
<ListView
contentContainerStyle={styles.grid}
dataSource={this.dataSource.cloneWithRows(prodcutList)}
renderRow={this.renderRow}
renderHeader={this.renderHeader}
enableEmptySections = {true}
/>
<View style={{
width: width,
height: 0.5,
backgroundColor: '#e5e5e5',
}}/>
<View style={{
width: width,
height: 15,
backgroundColor: '#f0f0f0',
}}/>
</View>
);
}
};
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;
const styles = StyleSheet.create({
grid: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
},
title: {
alignItems: 'center',
justifyContent: 'center',
height: 40,
width: width,
backgroundColor: 'white',
},
text: {
textAlign: 'left',
fontSize: 16,
fontWeight: 'bold',
color: '#000000',
marginTop: 16,
},
listContainer: {
width: width / 2,
},
thumbnail: {
position: 'absolute',
width: 40,
height: 38,
top: 1,
bottom: 1,
marginLeft: width - 40,
backgroundColor: 'transparent',
justifyContent: 'center',
},
});
... ...