Authored by 于良

增加coupon模块 review by shixiang

@@ -3,9 +3,10 @@ @@ -3,9 +3,10 @@
3 import Community from './community/Community'; 3 import Community from './community/Community';
4 import Plustar from './plustar/Plustar'; 4 import Plustar from './plustar/Plustar';
5 import QRCode from './qrcode/QRCode'; 5 import QRCode from './qrcode/QRCode';
  6 +import Coupon from './coupon/Coupon';
6 7
7 export default function native(platform) { 8 export default function native(platform) {
8 Community(platform); 9 Community(platform);
9 Plustar(platform); 10 Plustar(platform);
10 - QRCode(platform); 11 + Coupon(platform);
11 } 12 }
  1 +'use strict';
  2 +
  3 +import React from 'react';
  4 +import ReactNative from 'react-native';
  5 +
  6 +const {
  7 + Image,
  8 + Dimensions,
  9 + StyleSheet,
  10 +} = ReactNative;
  11 +
  12 +export default class AutoSizeImage extends React.Component {
  13 +
  14 + constructor(props) {
  15 + super (props);
  16 +
  17 + this.state = {
  18 + width: 0,
  19 + height: 0,
  20 + }
  21 + }
  22 +
  23 + componentDidMount() {
  24 + Image.getSize(this.props.src, (width, height) => {
  25 + let imgWidth = Dimensions.get('window').width;
  26 + let imgHeight = Math.ceil((height / width) * imgWidth);
  27 + this.setState({
  28 + width: imgWidth,
  29 + height: imgHeight,
  30 + });
  31 + });
  32 + }
  33 +
  34 + render() {
  35 + return (
  36 + <Image
  37 + source={{uri: this.props.src}}
  38 + style={{ width: this.state.width, height: this.state.height }}
  39 + resizeMode={'contain'}
  40 + />
  41 + );
  42 + }
  43 +}
@@ -4,34 +4,136 @@ import React, {Component} from 'react'; @@ -4,34 +4,136 @@ import React, {Component} from 'react';
4 import ReactNative, { 4 import ReactNative, {
5 View, 5 View,
6 Text, 6 Text,
  7 + Image,
  8 + ListView,
7 StyleSheet, 9 StyleSheet,
8 Dimensions, 10 Dimensions,
  11 + TouchableOpacity,
9 } from 'react-native'; 12 } from 'react-native';
10 13
  14 +import Banner from '../../../community/components/home/Banner';
  15 +import SlicedImage from '../../../common/components/SlicedImage';
  16 +import AutoSizeImage from './AutoSizeImage';
  17 +
11 export default class CouponCenter extends Component { 18 export default class CouponCenter extends Component {
12 19
13 constructor(props) { 20 constructor(props) {
14 super(props); 21 super(props);
15 22
  23 + this._renderRow = this._renderRow.bind(this);
16 24
  25 + this.dataSource = new ListView.DataSource({
  26 + rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
  27 + });
17 } 28 }
18 29
19 - componentDidMount() {  
20 - 30 + _renderRow(rowData: object, sectionID: number, rowID: number) {
  31 + let data = rowData.get('data');
  32 + switch (rowData.get('templateName')) {
  33 + case 'carousel_banner':
  34 + case 'focus':
  35 + return (
  36 + <Banner
  37 + data={data}
  38 + width={width}
  39 + height={bannerHeight}
  40 + onPress={this.props.onPressBanner}
  41 + />
  42 + );
  43 + case 'text':
  44 + return (
  45 + <Text style={styles.text}>{data}</Text>
  46 + );
  47 + case 'single_image':
  48 + let src = SlicedImage.getSlicedUrl(data.get('src'), 0, 0, 2);
  49 + return (
  50 + <TouchableOpacity
  51 + activeOpacity={0.8}
  52 + onPress={() => {
  53 + this.props.onPressImage && this.props.onPressImage(data.get('url'));
  54 + }}
  55 + >
  56 + <AutoSizeImage src={src}/>
  57 + </TouchableOpacity>
  58 + );
  59 + case 'getCoupon':
  60 + let bg = SlicedImage.getSlicedUrl(data.get('image').get('src'), 0, 0, 2);
  61 + let optImage;
  62 + if (data.get('status') == 1) {
  63 + optImage = require('../../images/click-txt.png');
  64 + } else if (data.get('status') == 2) {
  65 + optImage = require('../../images/zero.png');
  66 + } else if (data.get('status') == 3) {
  67 + optImage = require('../../images/received.png');
  68 + }
  69 + return (
  70 + <Image source={{uri: bg}} style={styles.couponContainer}>
  71 + <TouchableOpacity
  72 + style={styles.couponLeft}
  73 + activeOpacity={0.8}
  74 + onPress={() => {
  75 + this.props.onPressCoupon && this.props.onPressCoupon(data.get('image').get('url'));
  76 + }}
  77 + >
  78 + </TouchableOpacity>
  79 + <TouchableOpacity
  80 + style={styles.couponRight}
  81 + activeOpacity={0.8}
  82 + onPress={() => {
  83 + this.props.onGetCoupon && this.props.onGetCoupon(data.get('image').get('url'));
  84 + }}
  85 + >
  86 + <Image source={optImage} resizeMode={'center'}/>
  87 + </TouchableOpacity>
  88 + </Image>
  89 + );
  90 + default:
  91 + return null;
  92 + }
21 } 93 }
22 94
23 -  
24 render() { 95 render() {
25 - 96 + let data = this.props.floors.toArray();
26 return ( 97 return (
27 - <View/> 98 + <ListView
  99 + enableEmptySections={true}
  100 + dataSource={this.dataSource.cloneWithRows(data)}
  101 + renderRow={this._renderRow}
  102 + />
28 ); 103 );
29 } 104 }
30 } 105 }
31 106
  107 +let {width, height} = Dimensions.get('window');
  108 +let bannerHeight = Math.ceil((310 / 640) * width);
  109 +let couponHeight = Math.ceil((180 / 640) * width);
  110 +
32 let styles = StyleSheet.create({ 111 let styles = StyleSheet.create({
33 container: { 112 container: {
34 flex: 1, 113 flex: 1,
35 backgroundColor: '#f0f0f0', 114 backgroundColor: '#f0f0f0',
36 }, 115 },
  116 + textContainer: {
  117 +
  118 + },
  119 + text: {
  120 + fontFamily: 'helvetica',
  121 + fontSize: 18,
  122 + lineHeight: 56,
  123 + textAlign: 'center',
  124 + paddingBottom: (56 - 18) / 2,
  125 + },
  126 + couponContainer: {
  127 + width,
  128 + height: couponHeight,
  129 + flexDirection: 'row',
  130 + },
  131 + couponLeft: {
  132 + flex: 0.8,
  133 + },
  134 + couponRight: {
  135 + flex: 0.2,
  136 + justifyContent: 'center',
  137 +
  138 + },
37 }); 139 });
@@ -3,6 +3,11 @@ import keyMirror from 'key-mirror'; @@ -3,6 +3,11 @@ import keyMirror from 'key-mirror';
3 export default keyMirror({ 3 export default keyMirror({
4 4
5 SET_PLATFORM: null, 5 SET_PLATFORM: null,
  6 +
  7 + COUPON_CENTER_REQUEST: null,
  8 + COUPON_CENTER_SUCCESS: null,
  9 + COUPON_CENTER_FAILURE: null,
  10 +
6 JUMP_WITH_URL: null, 11 JUMP_WITH_URL: null,
7 12
8 }); 13 });
@@ -14,11 +14,11 @@ import { @@ -14,11 +14,11 @@ import {
14 import {bindActionCreators} from 'redux'; 14 import {bindActionCreators} from 'redux';
15 import {connect} from 'react-redux'; 15 import {connect} from 'react-redux';
16 import {Map} from 'immutable'; 16 import {Map} from 'immutable';
17 -import * as qrcodeActions from '../reducers/qrcode/qrcodeActions'; 17 +import * as couponActions from '../reducers/coupon/couponActions';
18 import CouponCenter from '../components/coupon/CouponCenter'; 18 import CouponCenter from '../components/coupon/CouponCenter';
19 19
20 const actions = [ 20 const actions = [
21 - qrcodeActions, 21 + couponActions,
22 ]; 22 ];
23 23
24 function mapStateToProps(state) { 24 function mapStateToProps(state) {
@@ -44,24 +44,48 @@ class CouponCenterContainer extends Component { @@ -44,24 +44,48 @@ class CouponCenterContainer extends Component {
44 constructor(props) { 44 constructor(props) {
45 super(props); 45 super(props);
46 46
  47 + this._onPressBanner = this._onPressBanner.bind(this);
  48 + this._onPressImage = this._onPressImage.bind(this);
  49 + this._onPressCoupon = this._onPressCoupon.bind(this);
  50 + this._onGetCoupon = this._onGetCoupon.bind(this);
47 } 51 }
48 52
49 componentDidMount() { 53 componentDidMount() {
50 - 54 + this.props.actions.couponCenter();
51 } 55 }
52 56
53 componentWillUnmount() { 57 componentWillUnmount() {
54 58
55 } 59 }
56 60
  61 + _onPressBanner(url) {
  62 + console.log(url);
  63 + this.props.actions.jumpWithUrl(url);
  64 + }
57 65
  66 + _onPressImage(url) {
  67 + this.props.actions.jumpWithUrl(url);
  68 + }
58 69
59 - render() { 70 + _onPressCoupon(url) {
  71 + this.props.actions.jumpWithUrl(url);
  72 + }
60 73
  74 + _onGetCoupon(url) {
  75 +
  76 + }
  77 +
  78 + render() {
  79 + let {isFetching, floors} = this.props.coupon;
61 return ( 80 return (
62 - <View style={styles.container}>  
63 - <CouponCenter/>  
64 - </View> 81 + <CouponCenter
  82 + isFetching={isFetching}
  83 + floors={floors}
  84 + onPressBanner={this._onPressBanner}
  85 + onPressImage={this._onPressImage}
  86 + onPressCoupon={this._onPressCoupon}
  87 + onGetCoupon={this._onGetCoupon}
  88 + />
65 ); 89 );
66 } 90 }
67 } 91 }
@@ -4,9 +4,151 @@ import ReactNative from 'react-native'; @@ -4,9 +4,151 @@ import ReactNative from 'react-native';
4 import CouponService from '../../services/CouponService'; 4 import CouponService from '../../services/CouponService';
5 5
6 const { 6 const {
  7 + COUPON_CENTER_REQUEST,
  8 + COUPON_CENTER_SUCCESS,
  9 + COUPON_CENTER_FAILURE,
7 JUMP_WITH_URL, 10 JUMP_WITH_URL,
8 } = require('../../constants/actionTypes').default; 11 } = require('../../constants/actionTypes').default;
9 12
  13 +export function couponCenterRequest() {
  14 + return {
  15 + type: COUPON_CENTER_REQUEST,
  16 + };
  17 +}
  18 +
  19 +export function couponCenterSuccess(json) {
  20 + return {
  21 + type: COUPON_CENTER_SUCCESS,
  22 + payload: json
  23 + };
  24 +}
  25 +
  26 +export function couponCenterFailure(error) {
  27 + return {
  28 + type: COUPON_CENTER_FAILURE,
  29 + payload: error
  30 + };
  31 +}
  32 +
  33 +/*
  34 + * index number 请求数据的index
  35 + * reload bool 是否需要强制重新请求数据,
  36 + */
  37 +export function couponCenter(reload = false) {
  38 + return (dispatch, getState) => {
  39 + let {app, coupon} = getState();
  40 + let {isFetching, floors, contentCode} = coupon;
  41 + if (reload) {
  42 + // 强制刷新数据
  43 +
  44 + } else {
  45 + if (isFetching || floors.size > 0) {
  46 + return;
  47 + }
  48 + }
  49 +
  50 + let fetchCouponInfo = (contentCode, uid) => {
  51 + return new CouponService().fetchFloors(contentCode, uid)
  52 + .then(json => {
  53 + let payload = parseFloors(json);
  54 + dispatch(couponCenterSuccess(payload));
  55 + })
  56 + .catch(error => {
  57 + dispatch(couponCenterFailure(error));
  58 + });
  59 + }
  60 +
  61 + dispatch(couponCenterRequest());
  62 + ReactNative.NativeModules.YH_CommonHelper.uid()
  63 + .then(uid => {
  64 + fetchCouponInfo(contentCode, uid);
  65 + })
  66 + .catch(error => {
  67 + fetchCouponInfo(contentCode, 0);
  68 + });
  69 + };
  70 +}
  71 +
  72 +function parseFloors(json) {
  73 + let carousel_banner = (data) => {
  74 + let images = [];
  75 + data && data.list && data.list.map((item, i) => {
  76 + let src = item.src ? item.src : '';
  77 + let url = item.url ? item.url : '';
  78 + images.push({src, url});
  79 + });
  80 + return images;
  81 + };
  82 +
  83 + let focus = (data) => {
  84 + let images = [];
  85 + data && data.map((item, i) => {
  86 + let src = item.src ? item.src : '';
  87 + let url = item.url ? item.url : '';
  88 + images.push({src, url});
  89 + });
  90 + return images;
  91 + };
  92 +
  93 + let text = (data) => {
  94 + let text = data && data.text ? data.text : '';
  95 + return text;
  96 + };
  97 +
  98 + let single_image = (data) => {
  99 + let src = data && data[0] && data[0].src ? data[0].src : '';
  100 + let url = data && data[0] && data[0].url ? data[0].url : '';
  101 + return {src, url};
  102 + };
  103 +
  104 + let getCoupon = (data) => {
  105 + let coupon = data && data[0] ? data[0] : {};
  106 + let text = coupon.text ? coupon.text : '';
  107 + let couponID = coupon.couponID ? coupon.couponID : '';
  108 + let image = {
  109 + src : coupon.image && coupon.image.src ? coupon.image.src : '',
  110 + url : coupon.image && coupon.image.url ? coupon.image.url : '',
  111 + };
  112 + let isShow = coupon.isShow;
  113 + let status = coupon.status;
  114 + return {
  115 + text,
  116 + couponID,
  117 + image,
  118 + isShow,
  119 + status,
  120 + };
  121 + };
  122 +
  123 + let floors = [];
  124 + json && json.map((item, i) => {
  125 + let templateName = item.template_name;
  126 + if (!templateName) {
  127 + templateName = item.templateName;
  128 + }
  129 +
  130 + let data;
  131 + if (templateName == 'carousel_banner') {
  132 + data = carousel_banner(item.data);
  133 + } else if (templateName == 'focus') {
  134 + data = focus(item.data)
  135 + } else if (templateName == 'text') {
  136 + data = text(item.data)
  137 + } else if (templateName == 'single_image') {
  138 + data = single_image(item.data)
  139 + } else if (templateName == 'getCoupon') {
  140 + data = getCoupon(item.data)
  141 + }
  142 +
  143 + let floor = {
  144 + data,
  145 + templateName,
  146 + };
  147 + floors.push(floor);
  148 + });
  149 +
  150 + return floors;
  151 +}
10 152
11 export function jumpWithUrl(url) { 153 export function jumpWithUrl(url) {
12 if (!url) { 154 if (!url) {
@@ -14,7 +156,7 @@ export function jumpWithUrl(url) { @@ -14,7 +156,7 @@ export function jumpWithUrl(url) {
14 return; 156 return;
15 } 157 }
16 158
17 - ReactNative.NativeModules.YH_PlustarHelper.jumpWithUrl(url); 159 + ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
18 return { 160 return {
19 type: JUMP_WITH_URL, 161 type: JUMP_WITH_URL,
20 payload: url 162 payload: url
@@ -3,23 +3,10 @@ @@ -3,23 +3,10 @@
3 import {Record, List, Map} from 'immutable'; 3 import {Record, List, Map} from 'immutable';
4 4
5 let InitialState = Record({ 5 let InitialState = Record({
6 - activeTab: 0,  
7 - gender: -1,  
8 - 0: new (Record({  
9 - isFetching: false,  
10 - error: null,  
11 - head: List(),  
12 - foot: List(),  
13 - list: List(),  
14 - })),  
15 - 1: new (Record({  
16 - isFetching: false,  
17 - error: null,  
18 - head: List(),  
19 - foot: List(),  
20 - list: List(),  
21 - })),  
22 - segment: null, 6 + contentCode: 'b78b32ed81b18dde8ac84fd33602b88b',
  7 + isFetching: false,
  8 + error: null,
  9 + floors: List(),
23 }); 10 });
24 11
25 export default InitialState; 12 export default InitialState;
@@ -4,6 +4,9 @@ import InitialState from './couponInitialState'; @@ -4,6 +4,9 @@ import InitialState from './couponInitialState';
4 import Immutable, {Map} from 'immutable'; 4 import Immutable, {Map} from 'immutable';
5 5
6 const { 6 const {
  7 + COUPON_CENTER_REQUEST,
  8 + COUPON_CENTER_SUCCESS,
  9 + COUPON_CENTER_FAILURE,
7 JUMP_WITH_URL, 10 JUMP_WITH_URL,
8 } = require('../../constants/actionTypes').default; 11 } = require('../../constants/actionTypes').default;
9 12
@@ -11,7 +14,21 @@ const initialState = new InitialState; @@ -11,7 +14,21 @@ const initialState = new InitialState;
11 14
12 export default function couponReducer(state=initialState, action) { 15 export default function couponReducer(state=initialState, action) {
13 switch(action.type) { 16 switch(action.type) {
  17 + case COUPON_CENTER_REQUEST: {
  18 + return state.set('isFetching', true)
  19 + .set('error', null);
  20 + }
14 21
  22 + case COUPON_CENTER_SUCCESS: {
  23 + return state.set('isFetching', false)
  24 + .set('error', null)
  25 + .set('floors', Immutable.fromJS(action.payload));
  26 + }
  27 +
  28 + case COUPON_CENTER_FAILURE: {
  29 + return state.set('isFetching', false)
  30 + .set('error', action.payload);
  31 + }
15 } 32 }
16 33
17 return state; 34 return state;
@@ -5,16 +5,17 @@ import Request from '../../common/services/Request'; @@ -5,16 +5,17 @@ import Request from '../../common/services/Request';
5 export default class CouponService { 5 export default class CouponService {
6 6
7 constructor () { 7 constructor () {
8 - let baseURL = 'http://service.yoho.cn'; 8 + let baseURL = 'http://api.yoho.cn';
9 this.api = new Request(baseURL); 9 this.api = new Request(baseURL);
10 } 10 }
11 11
12 - async fetchList(brand_type, options) { 12 + async fetchFloors(contentCode, uid) {
13 return await this.api.get({ 13 return await this.api.get({
14 - url: '/guang/api/v3/plustar/getlist', 14 + url: '',
15 body: { 15 body: {
16 - brand_type,  
17 - ...options, 16 + method: 'app.promotion.queryCouponCenter',
  17 + contentCode,
  18 + uid,
18 } 19 }
19 }) 20 })
20 .then((json) => { 21 .then((json) => {