Authored by 孙凯

add 拼团 review by daiqiang

... ... @@ -32,6 +32,7 @@ import Setting from './setting/Setting'
import AssociatorGift from './associatorGift/AssociatorGift'
import UserLogout from './userLogout/UserLogout'
import Alliance from './alliance/Alliance'
import GroupPurchase from './groupPurchase/GroupPurchase'
console.disableYellowBox = true
... ... @@ -63,7 +64,8 @@ export default function native(platform) {
AssociatorGift(platform);
UserLogout(platform);
Alliance(platform);
GroupPurchase(platform);
if (Platform.OS === 'ios') {
// Community(platform);
QRCode(platform);
... ...
'use strict';
import React from 'react';
import {AppRegistry} from 'react-native';
import {Provider} from 'react-redux';
import configureStore from './store/configureStore';
import appInitialState from './reducers/app/appInitialState';
import groupPurchaseInitialState from './reducers/groupPurchase/groupPurchaseInitialState';
import GroupPurchaseContainer from './containers/GroupPurchaseContainer';
import {setHost, setPlatform, setServiceHost} from './reducers/app/appActions';
import {setActivityId} from './reducers/groupPurchase/groupPurchaseActions';
import createReactClass from 'create-react-class';
function getInitialState() {
const _initState = {
app: (new appInitialState()),
groupPurchase: (new groupPurchaseInitialState()),
};
return _initState;
}
export default function native(platform) {
let YH_GroupPurchase = createReactClass({
render() {
const store = configureStore(getInitialState());
store.dispatch(setPlatform(platform));
store.dispatch(setHost(this.props.host));
store.dispatch(setServiceHost(this.props.serviceHost));
store.dispatch(setActivityId(this.props.activityId));
let type = this.props.type;
if (type === 'List') {
return (
<Provider store={store}>
<GroupPurchaseContainer/>
</Provider>
);
} else if (type === 'detail') {
// return (
// <Provider store={store}>
// <OrderIncomeContainer/>
// </Provider>
// );
}
}
});
AppRegistry.registerComponent('YH_GroupPurchase', () => YH_GroupPurchase);
}
... ...
'use strict';
import React from 'react';
import Immutable, {Map} from 'immutable';
import YH_Image from '../../common/components/YH_Image';
import {getSlicedUrl} from '../../classify/utils/Utils';
import DeleteLineText from '../../common/components/DeleteLineText';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
export default class GroupProductCell extends React.Component {
constructor(props) {
super(props);
}
render() {
let { resource } = this.props;
if (!resource) {
return null;
}
let newSrc = getSlicedUrl(resource.get('defaultImages'), 200, 260, 2);
let deleteLineText = '¥' + resource.get('marketPrice');
return (
<View style={styles.cell}>
<View style={styles.left}>
<YH_Image style={styles.activityImage} url={newSrc}></YH_Image>
</View>
<View style={styles.right}>
<View style={styles.titleView}>
<Text style={styles.title} numberOfLines={2}>{resource.get('productName')}</Text>
</View>
<View style={styles.priceView}>
<Text style={styles.iconText}>{resource.get('peopleNum')}人团</Text>
<Text style={styles.price1}>¥{resource.get('collagePrice')}</Text>
<DeleteLineText
style={styles.oldPriceContainer}
textStyle={styles.price2}
lineStyle={styles.deleteLine}
text={deleteLineText}
/>
</View>
<View style={styles.buttonView}>
<Text style={styles.button1}>{resource.get('peopleNum')}人直降</Text>
<Text style={styles.button2}>立即购买</Text>
</View>
</View>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let styles = StyleSheet.create({
cell : {
width,
height: 151*DEVICE_WIDTH_RATIO,
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
left :{
width: 115*DEVICE_WIDTH_RATIO,
height: 150*DEVICE_WIDTH_RATIO,
},
activityImage :{
width: 100*DEVICE_WIDTH_RATIO,
height: 130*DEVICE_WIDTH_RATIO,
marginTop: 10*DEVICE_WIDTH_RATIO,
marginLeft: 15*DEVICE_WIDTH_RATIO,
},
right :{
width: 260*DEVICE_WIDTH_RATIO,
height: 150*DEVICE_WIDTH_RATIO,
},
titleView: {
width: 235*DEVICE_WIDTH_RATIO,
height: 40*DEVICE_WIDTH_RATIO,
marginTop: 12*DEVICE_WIDTH_RATIO,
marginLeft: 10*DEVICE_WIDTH_RATIO,
},
title: {
fontSize: 14,
color: '#444444',
lineHeight: 19,
},
priceView: {
width: 235*DEVICE_WIDTH_RATIO,
height: 25*DEVICE_WIDTH_RATIO,
marginLeft: 10*DEVICE_WIDTH_RATIO,
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
iconText: {
width: 40,
height: 14,
backgroundColor: '#D0021B',
color: 'white',
fontSize: 8,
lineHeight: 14,
textAlign: 'center',
lineHeight: 14,
},
price1: {
fontSize: 18,
marginLeft: 10*DEVICE_WIDTH_RATIO,
color: '#444444',
lineHeight: 18*DEVICE_WIDTH_RATIO,
},
oldPriceContainer: {
flexDirection: 'row',
height: 12,
marginLeft: 10*DEVICE_WIDTH_RATIO,
marginTop: 5,
},
price2: {
fontSize: 12,
lineHeight: 12,
color: '#B0B0B0',
alignItems: 'center',
},
buttonView: {
width: 235*DEVICE_WIDTH_RATIO,
height: 30*DEVICE_WIDTH_RATIO,
marginBottom: 10*DEVICE_WIDTH_RATIO,
marginLeft: 10*DEVICE_WIDTH_RATIO,
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
},
button1: {
width: 60*DEVICE_WIDTH_RATIO,
height: 30*DEVICE_WIDTH_RATIO,
fontSize: 12,
color: '#D0021B',
borderWidth:1,
borderColor: '#D0021B',
borderBottomLeftRadius: 15*DEVICE_WIDTH_RATIO,
borderTopLeftRadius: 25*DEVICE_WIDTH_RATIO,
lineHeight: 30*DEVICE_WIDTH_RATIO,
textAlign: 'center',
backgroundColor: 'white',
},
button2: {
width: 60*DEVICE_WIDTH_RATIO,
height: 30*DEVICE_WIDTH_RATIO,
fontSize: 12,
color: 'white',
borderBottomRightRadius: 15*DEVICE_WIDTH_RATIO,
borderTopRightRadius: 15*DEVICE_WIDTH_RATIO,
textAlign: 'center',
lineHeight: 30*DEVICE_WIDTH_RATIO,
backgroundColor: '#D0021B',
},
deleteLine: {
position: 'absolute',
top: (12 / 2) - 0.8,
left: 0,
right: 0,
height: 1,
backgroundColor: '#b0b0b0',
},
});
... ...
'use strict';
import React, {Component} from 'react';
import {Dimensions, Image, ListView, StyleSheet, Text, TouchableOpacity, View, NativeModules} from 'react-native';
import {Immutable} from 'immutable';
import GroupProductCell from './GroupProductCell'
import YH_Image from '../../common/components/YH_Image';
import {getSlicedUrl} from '../../classify/utils/Utils';
export default class GroupPurchase extends 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),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
}
_renderHeader() {
let {
resource,
} = this.props;
let resourceJS = resource.toJS();
if(!resourceJS.data) return null;
let datss = resourceJS.data;
let data = datss[0].data;
let imageHeight = data.imageHeight;
let imageWidth = data.imageWidth;
let list = data.list;
let src = list[0].src;
let url = list[0].url;
let newSrc = getSlicedUrl(src, imageWidth, imageHeight, 2);
return (
<View style={styles.header}>
<YH_Image style={styles.header} url={newSrc}></YH_Image>
</View>
);
}
_renderRow(rowData, sectionID, rowID) {
return (
<GroupProductCell resource={rowData}/>
);
}
render() {
let {
productList,
resource,
} = this.props;
let dataSource = {
productList: productList.list ? productList.list.toArray() : [],
};
return (
<View style={styles.container}>
<ListView
ref={(c) => {
this.listView = c;
}}
enableEmptySections={true}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
onEndReached={() => {
if (productList.list.size !== 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
width,
height: 121*DEVICE_WIDTH_RATIO,
},
})
;
... ...
import keyMirror from 'key-mirror';
export default keyMirror({
SET_PLATFORM: null,
SET_HOST: null,
SET_SERVICE_HOST: null,
SET_ACTIVITY_ID: null,
PRODUCT_LIST_REQUEST: null,
PRODUCT_LIST_SUCCESS: null,
PRODUCT_LIST_FAILURE: null,
RESOURCEINFO_REQUEST: null,
RESOURCEINFO_SUCCESS: null,
RESOURCEINFO_FAILURE: null,
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {Platform, StyleSheet, View} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {Map} from 'immutable';
import * as groupPurchaseActions from '../reducers/groupPurchase/groupPurchaseActions';
import GroupPurchase from '../components/GroupPurchase'
const actions = [
groupPurchaseActions,
];
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 GroupPurchaseContainer extends Component {
constructor(props) {
super(props);
this._onEndReached = this._onEndReached.bind(this);
}
componentDidMount() {
this.props.actions.getProductList();
this.props.actions.fetchResourceInfo();
}
componentWillUnmount() {
}
_onEndReached() {
this.props.actions.getProductList();
}
render() {
let {
productList,
resource,
} = this.props.groupPurchase;
return (
<View style={styles.container}>
<GroupPurchase
productList={productList}
resource={resource}
onEndReached={this._onEndReached}
/>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps)(GroupPurchaseContainer);
... ...
'use strict';
const {
SET_PLATFORM,
SET_HOST,
SET_SERVICE_HOST
} = require('../../constants/actionTypes').default;
export function setPlatform(platform) {
return {
type: SET_PLATFORM,
payload: platform
};
}
export function setHost(host) {
return {
type: SET_HOST,
payload: host,
}
}
export function setServiceHost(host) {
return {
type: SET_SERVICE_HOST,
payload: host
};
}
... ...
'use strict';
import {Record} from 'immutable';
let InitialState = Record({
platform: 'ios', // ios, android
host: 'http://api.yoho.cn',
serviceHost: 'http://api.yoho.cn',
});
export default InitialState;
... ...
'use strict';
import InitialState from './appInitialState';
const {
SET_PLATFORM,
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_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 GroupPurchaseService from '../../services/GroupPurchaseService';
const Platform = require('Platform');
const {
SET_ACTIVITY_ID,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
RESOURCEINFO_REQUEST,
RESOURCEINFO_SUCCESS,
RESOURCEINFO_FAILURE,
} = require('../../constants/actionTypes').default;
export function setActivityId(activityId) {
return {
type: SET_ACTIVITY_ID,
payload: activityId,
}
}
export function productListRequest() {
return {
type: PRODUCT_LIST_REQUEST,
};
}
export function productListSuccess(json) {
return {
type: PRODUCT_LIST_SUCCESS,
payload: json
}
}
export function productListFailure(error) {
return {
type: PRODUCT_LIST_FAILURE,
payload: error
}
}
export function resourceInfoRequest() {
return {
type: RESOURCEINFO_REQUEST,
};
}
export function resourceInfoSuccess(json) {
return {
type: RESOURCEINFO_SUCCESS,
payload: json
}
}
export function resourceInfoFailure(error) {
return {
type: RESOURCEINFO_FAILURE,
payload: error
}
}
export function fetchResourceInfo() {
return (dispatch, getState) => {
let {app} = getState();
dispatch(resourceInfoRequest());
return new GroupPurchaseService(app.host).fetchResourceInfo()
.then(json => {
dispatch(resourceInfoSuccess(json));
})
.catch(error => {
dispatch(resourceInfoFailure(error));
});
};
}
export function getProductList(reload=false) {
return (dispatch, getState) => {
let {app, groupPurchase} = getState();
let {productList,activityId} = groupPurchase;
if (reload) {
} else {
if (productList.isFetching || productList.endReached || productList.error) {
return;
}
}
let page = productList.page + 1;
let limit = productList.pageSize;
dispatch(productListRequest());
return new GroupPurchaseService(app.host).fetchProductList(activityId, page, limit)
.then(json => {
json.endReached = json.page == json.page_total;
if (json.page > 1) {
let oldList = productList.list.toJS();
let list = [...oldList, ...json.collageProductVoList];
json.collageProductVoList = list;
}
dispatch(productListSuccess(json));
})
.catch(error => {
dispatch(productListFailure(error));
});
};
}
... ...
'use strict';
import {List, Map, Record} from 'immutable';
let InitialState = Record({
activityId: 0,
productList: new (Record({
isFetching: false,
error: null,
list: List(),
page: 0,
total: 0,
pagetotal: 0,
pageSize: 2,//60,
endReached: false,
})),
resource: new (Record({
isFetching: false,
error: null,
data: null,
})),
});
export default InitialState;
... ...
'use strict';
import InitialState from './groupPurchaseInitialState';
import Immutable from 'immutable';
const {
SET_ACTIVITY_ID,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAILURE,
RESOURCEINFO_REQUEST,
RESOURCEINFO_SUCCESS,
RESOURCEINFO_FAILURE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function couponReducer(state = initialState, action) {
if (!(state instanceof InitialState)) {
return initialState.merge(state);
}
switch (action.type) {
case SET_ACTIVITY_ID: {
return state.set('activityId', action.payload);
}
case PRODUCT_LIST_REQUEST: {
return state.setIn(['productList', 'isFetching'], true)
.setIn(['productList', 'error'], null);
}
case PRODUCT_LIST_SUCCESS: {
let {
collageProductVoList,
page,
page_total,
total,
endReached,
} = action.payload;
return state.setIn(['productList', 'page'], page)
.setIn(['productList', 'pagetotal'], page_total)
.setIn(['productList', 'total'], total)
.setIn(['productList', 'list'], Immutable.fromJS(collageProductVoList))
.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], null)
.setIn(['productList', 'endReached'], endReached);
}
case PRODUCT_LIST_FAILURE: {
return state.setIn(['productList', 'isFetching'], false)
.setIn(['productList', 'error'], action.payload);
}
case RESOURCEINFO_REQUEST: {
return state.setIn(['resource', 'isFetching'], true)
.setIn(['resource', 'error'], null);
}
case RESOURCEINFO_SUCCESS: {
return state.setIn(['resource', 'isFetching'], false)
.setIn(['resource', 'data'], Immutable.fromJS(action.payload));
}
case RESOURCEINFO_FAILURE: {
return state.setIn(['resource', 'isFetching'], false)
.setIn(['resource', 'error'], action.payload);
}
}
return state;
}
... ...
import {combineReducers} from 'redux';
import app from './app/appReducer';
import groupPurchase from './groupPurchase/groupPurchaseReducer';
const rootReducer = combineReducers({
app,
groupPurchase,
});
export default rootReducer;
... ...
'use strict';
import Request from '../../common/services/NativeRequest';
import {Platform} from "react-native";
export default class groupPurchaseService {
constructor(host) {
// let baseURL = 'http://api.yoho.cn';
// if (host) {
// baseURL = host;
// }
// this.api = new Request(baseURL);
this.api = new Request('http://api-test3.dev.yohocorp.com');
}
async fetchProductList(activityId,page=1, limit=20) {
return await this.api.get({
url: '',
body: {
method: 'app.collage.productList.page',
page,
limit,
activityId,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
//获取资源位
async fetchResourceInfo() {
//content_code 测试环境:f99fa98a01fdadb09012bbd2b4a28e91
// 正式环境:ab4ca2161bada40fe7335dce51b8e196
return await this.api.get({
url: '/operations/api/v5/resource/get',
body: {
content_code: 'f99fa98a01fdadb09012bbd2b4a28e91',
fromPage: ''
}
})
.then((json) => {
console.log(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);
};
... ...