Showing
21 changed files
with
504 additions
and
50 deletions
1 | import React, { Component } from 'react'; | 1 | import React, { Component } from 'react'; |
2 | -import { | 2 | +import ReactNative, { |
3 | + Dimensions, | ||
4 | + Image, | ||
3 | SectionList, | 5 | SectionList, |
4 | StyleSheet, | 6 | StyleSheet, |
5 | Text, | 7 | Text, |
@@ -10,30 +12,70 @@ import { | @@ -10,30 +12,70 @@ import { | ||
10 | 12 | ||
11 | import CouponListCell from './CouponListCell'; | 13 | import CouponListCell from './CouponListCell'; |
12 | 14 | ||
15 | +let {width, height} = Dimensions.get('window'); | ||
16 | +const DEVICE_WIDTH_RATIO = width / 375; | ||
17 | + | ||
13 | export default class CouponList extends Component { | 18 | export default class CouponList extends Component { |
14 | state = { } | 19 | state = { } |
15 | 20 | ||
16 | render() { | 21 | render() { |
17 | let data = [{title: '', data: this.props.data || []}]; | 22 | let data = [{title: '', data: this.props.data || []}]; |
18 | - return ( | ||
19 | - <SectionList | ||
20 | - style={styles.container} | ||
21 | - renderItem={this.renderItem} | ||
22 | - sections={data} | ||
23 | - /> | ||
24 | - ); | 23 | + if (!this.props.isFetching && data[0].data.length == 0) { |
24 | + return ( | ||
25 | + <View style={styles.emptyContainer}> | ||
26 | + <Image source={require('../../images/coupon.png')} style={styles.emptyImage} /> | ||
27 | + <Text style={styles.couponErrorPageText}>暂无优惠券</Text> | ||
28 | + </View> | ||
29 | + ) | ||
30 | + } else { | ||
31 | + return ( | ||
32 | + <SectionList | ||
33 | + style={styles.container} | ||
34 | + contentContainerStyle={styles.contentContainerStyle} | ||
35 | + renderItem={this.renderItem} | ||
36 | + sections={data} | ||
37 | + onEndReached={this.props.onEndReached} | ||
38 | + keyExtractor={(item, index) => '' + index} | ||
39 | + /> | ||
40 | + ); | ||
41 | + } | ||
25 | } | 42 | } |
26 | 43 | ||
27 | renderItem = ({item, index}) => { | 44 | renderItem = ({item, index}) => { |
28 | return ( | 45 | return ( |
29 | - <CouponListCell /> | 46 | + <CouponListCell data={item} type={this.props.type} goCouponProductList={() => this.goCouponProductList(item)}/> |
30 | ) | 47 | ) |
31 | } | 48 | } |
49 | + | ||
50 | + goCouponProductList(item) { | ||
51 | + let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.couponProductList", | ||
52 | + "params":{"coupon_id":"${item.coupon_id}","coupon_code":"${item.coupon_code}"}, "coupon_title":"${item.coupon_name}"}`; | ||
53 | + ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url); | ||
54 | + } | ||
32 | } | 55 | } |
33 | 56 | ||
34 | const styles = StyleSheet.create({ | 57 | const styles = StyleSheet.create({ |
35 | container: { | 58 | container: { |
36 | flex: 1, | 59 | flex: 1, |
37 | backgroundColor: '#f0f0f0', | 60 | backgroundColor: '#f0f0f0', |
61 | + }, | ||
62 | + emptyContainer: { | ||
63 | + flex: 1, | ||
64 | + backgroundColor: '#f0f0f0', | ||
65 | + justifyContent: 'center', | ||
66 | + alignItems: 'center', | ||
67 | + }, | ||
68 | + contentContainerStyle: { | ||
69 | + paddingVertical: 5 * DEVICE_WIDTH_RATIO | ||
70 | + }, | ||
71 | + emptyImage: { | ||
72 | + width: 104 * DEVICE_WIDTH_RATIO, | ||
73 | + height: 65 * DEVICE_WIDTH_RATIO, | ||
74 | + // marginTop: 131 * DEVICE_WIDTH_RATIO, | ||
75 | + marginBottom: 15 * DEVICE_WIDTH_RATIO, | ||
76 | + }, | ||
77 | + couponErrorPageText: { | ||
78 | + fontSize: 14, | ||
79 | + color: '#b0b0b0', | ||
38 | } | 80 | } |
39 | }) | 81 | }) |
1 | import React, { Component } from 'react'; | 1 | import React, { Component } from 'react'; |
2 | import { | 2 | import { |
3 | + Dimensions, | ||
4 | + Image, | ||
3 | SectionList, | 5 | SectionList, |
4 | StyleSheet, | 6 | StyleSheet, |
5 | Text, | 7 | Text, |
@@ -8,18 +10,240 @@ import { | @@ -8,18 +10,240 @@ import { | ||
8 | View, | 10 | View, |
9 | } from 'react-native'; | 11 | } from 'react-native'; |
10 | 12 | ||
13 | +let {width, height} = Dimensions.get('window'); | ||
14 | +const DEVICE_WIDTH_RATIO = width / 375; | ||
15 | + | ||
11 | export default class CouponListCell extends Component { | 16 | export default class CouponListCell extends Component { |
12 | state = { } | 17 | state = { } |
13 | render() { | 18 | render() { |
19 | + let { type } = this.props; | ||
20 | + let data = this.props.data || {}; | ||
21 | + let coupon_value = data && data.coupon_value_str ? data.coupon_value_str : 0; | ||
22 | + let coupon_name = data && data.coupon_name ? data.coupon_name : ''; | ||
23 | + let coupon_validity = data && data.coupon_validity ? data.coupon_validity : ''; | ||
24 | + let use_rule = data && data.use_rule ? data.use_rule : ''; | ||
25 | + let notes = data && data.notes ? data.notes : []; | ||
26 | + let { catalog, catalog_name, is_online_avail, is_overdue_soon } = data; | ||
27 | + let image, color; | ||
28 | + if (catalog == '100') { | ||
29 | + image = require('../../images/bgyellow.png'); | ||
30 | + color = '#ffa72e'; | ||
31 | + } else if (catalog == '200') { | ||
32 | + image = require('../../images/bgred.png'); | ||
33 | + color = '#fc5960'; | ||
34 | + } else { | ||
35 | + image = require('../../images/bgblack.png'); | ||
36 | + color = '#000000'; | ||
37 | + } | ||
38 | + let arrowImage = this.state.showDetail ? require('../../images/up.png') : require('../../images/down.png'); | ||
39 | + let grayImage = type == 'use' ? require('../../images/yishiyong.png') : type == 'overtime' ? require('../../images/guoqi.png') : require('../../images/yishiyong.png'); | ||
40 | + if (type != 'notuse') { | ||
41 | + color = '#b0b0b0'; | ||
42 | + image = require('../../images/bggrey.png'); | ||
43 | + } | ||
14 | return ( | 44 | return ( |
15 | - <View /> | 45 | + <View style={styles.container}> |
46 | + <View style={[styles.row, this.state.showDetail && styles.shadow]}> | ||
47 | + <View> | ||
48 | + <Image source={image} resizeMode="stretch" style={styles.bgImg}/> | ||
49 | + <View style={styles.leftAb}> | ||
50 | + <Text style={[styles.price, {color}, (data.coupon_value != data.coupon_value_str) && {fontSize: 24 * DEVICE_WIDTH_RATIO}]} numberOfLines={1}> | ||
51 | + {coupon_value} | ||
52 | + </Text> | ||
53 | + {use_rule ? <Text style={[styles.priceDetail, {color}]}> | ||
54 | + {use_rule} | ||
55 | + </Text> : null} | ||
56 | + </View> | ||
57 | + </View> | ||
58 | + <View style={styles.right}> | ||
59 | + <Text style={[styles.titleTip, {color}]} numberOfLines={2} > | ||
60 | + [{catalog_name}] | ||
61 | + <Text style={[styles.title, type != 'notuse' && {color}]} > | ||
62 | + {' '} {coupon_name} | ||
63 | + </Text> | ||
64 | + </Text> | ||
65 | + <Text style={styles.time}> | ||
66 | + {coupon_validity} | ||
67 | + </Text> | ||
68 | + <TouchableOpacity | ||
69 | + activeOpacity={1.0} | ||
70 | + onPress={() => { | ||
71 | + this.setState({showDetail: !this.state.showDetail}) | ||
72 | + }}> | ||
73 | + <View style={styles.bottom}> | ||
74 | + <Text style={styles.bottomText}> | ||
75 | + 使用说明 | ||
76 | + </Text> | ||
77 | + <Image source={arrowImage} style={styles.bottomIcon} resizeMode="contain"/> | ||
78 | + </View> | ||
79 | + </TouchableOpacity> | ||
80 | + {(type == 'notuse' && is_online_avail) ? <TouchableOpacity onPress={this.props.goCouponProductList} style={styles.immediatelyUse}> | ||
81 | + <Text style={styles.immediatelyUseText}>立即使用</Text> | ||
82 | + </TouchableOpacity> : null} | ||
83 | + {type != 'notuse' ? <Image source={grayImage} style={styles.grayImage} /> : null} | ||
84 | + {is_overdue_soon ? <Image source={require('../../images/tip.png')} style={styles.overDueSoon} resizeMode="stretch" /> : null} | ||
85 | + </View> | ||
86 | + </View> | ||
87 | + {this.state.showDetail ? | ||
88 | + <View style={styles.detail}> | ||
89 | + {notes.map((item, i) => { | ||
90 | + return ( | ||
91 | + <View key={i} style={styles.detailTextView}> | ||
92 | + <Text style={styles.detailText} numberOfLines={2}> | ||
93 | + {item} | ||
94 | + </Text> | ||
95 | + </View> | ||
96 | + ) | ||
97 | + })} | ||
98 | + </View> | ||
99 | + : null | ||
100 | + } | ||
101 | + </View> | ||
16 | ); | 102 | ); |
17 | } | 103 | } |
18 | } | 104 | } |
19 | 105 | ||
20 | const styles = StyleSheet.create({ | 106 | const styles = StyleSheet.create({ |
21 | container: { | 107 | container: { |
22 | - flex: 1, | ||
23 | - backgroundColor: '#f0f0f0', | 108 | + marginVertical: 5 * DEVICE_WIDTH_RATIO, |
109 | + marginHorizontal: 10 * DEVICE_WIDTH_RATIO, | ||
110 | + }, | ||
111 | + row: { | ||
112 | + flexDirection: 'row', | ||
113 | + zIndex: 1, | ||
114 | + }, | ||
115 | + bgImg: { | ||
116 | + width: 110 * DEVICE_WIDTH_RATIO, | ||
117 | + height: 100 * DEVICE_WIDTH_RATIO, | ||
118 | + }, | ||
119 | + right: { | ||
120 | + width: 245 * DEVICE_WIDTH_RATIO, | ||
121 | + height: 100 * DEVICE_WIDTH_RATIO, | ||
122 | + backgroundColor: '#ffffff', | ||
123 | + borderTopRightRadius: 4 * DEVICE_WIDTH_RATIO, | ||
124 | + borderBottomRightRadius: 4 * DEVICE_WIDTH_RATIO, | ||
125 | + }, | ||
126 | + price: { | ||
127 | + fontSize: 30 * DEVICE_WIDTH_RATIO, | ||
128 | + color: '#002B47', | ||
129 | + letterSpacing: 0, | ||
130 | + textAlign: 'center', | ||
131 | + fontWeight: 'bold', | ||
132 | + }, | ||
133 | + priceDetail: { | ||
134 | + fontFamily: 'PingFang-SC-Regular', | ||
135 | + fontSize: 12*DEVICE_WIDTH_RATIO, | ||
136 | + color: '#002B47', | ||
137 | + letterSpacing: 0, | ||
138 | + textAlign: 'center', | ||
139 | + }, | ||
140 | + leftAb: { | ||
141 | + position: 'absolute', | ||
142 | + top: 0, | ||
143 | + left: 8*DEVICE_WIDTH_RATIO, | ||
144 | + right: 5*DEVICE_WIDTH_RATIO, | ||
145 | + bottom: 0, | ||
146 | + justifyContent: 'center', | ||
147 | + alignItems: 'center', | ||
148 | + }, | ||
149 | + titleTip: { | ||
150 | + fontFamily: 'PingFang-SC-Medium', | ||
151 | + fontSize: 12*DEVICE_WIDTH_RATIO, | ||
152 | + color: '#002B47', | ||
153 | + letterSpacing: 0, | ||
154 | + marginLeft: 10*DEVICE_WIDTH_RATIO, | ||
155 | + marginTop: 10*DEVICE_WIDTH_RATIO, | ||
156 | + height: 35*DEVICE_WIDTH_RATIO, | ||
157 | + maxWidth: 185*DEVICE_WIDTH_RATIO, | ||
158 | + fontWeight: 'bold' | ||
159 | + }, | ||
160 | + title: { | ||
161 | + fontFamily: 'PingFang-SC-Regular', | ||
162 | + fontSize: 12*DEVICE_WIDTH_RATIO, | ||
163 | + color: '#444444', | ||
164 | + fontWeight: 'normal', | ||
165 | + letterSpacing: 0, | ||
166 | + lineHeight: 15*DEVICE_WIDTH_RATIO, | ||
167 | + }, | ||
168 | + time: { | ||
169 | + marginLeft: 10*DEVICE_WIDTH_RATIO, | ||
170 | + fontFamily: 'PingFang-SC-Regular', | ||
171 | + fontSize: 11*DEVICE_WIDTH_RATIO, | ||
172 | + color: '#b0b0b0', | ||
173 | + letterSpacing: 0, | ||
174 | + | ||
175 | + }, | ||
176 | + bottom: { | ||
177 | + marginTop: 5*DEVICE_WIDTH_RATIO, | ||
178 | + height: 35*DEVICE_WIDTH_RATIO, | ||
179 | + flexDirection: 'row', | ||
180 | + marginLeft: 10*DEVICE_WIDTH_RATIO, | ||
181 | + alignItems: 'center', | ||
182 | + }, | ||
183 | + bottomText: { | ||
184 | + fontFamily: 'PingFang-SC-Regular', | ||
185 | + fontSize: 11*DEVICE_WIDTH_RATIO, | ||
186 | + color: '#B0B0B0', | ||
187 | + letterSpacing: 0, | ||
188 | + }, | ||
189 | + bottomIcon: { | ||
190 | + width: 10*DEVICE_WIDTH_RATIO, | ||
191 | + height: 10*DEVICE_WIDTH_RATIO, | ||
192 | + marginLeft: 5*DEVICE_WIDTH_RATIO, | ||
193 | + }, | ||
194 | + immediatelyUse: { | ||
195 | + position: 'absolute', | ||
196 | + right: 10*DEVICE_WIDTH_RATIO, | ||
197 | + bottom: 10*DEVICE_WIDTH_RATIO, | ||
198 | + width: 65*DEVICE_WIDTH_RATIO, | ||
199 | + height: 25*DEVICE_WIDTH_RATIO, | ||
200 | + borderWidth: 1, | ||
201 | + borderColor: '#444444', | ||
202 | + borderRadius: 12.5*DEVICE_WIDTH_RATIO, | ||
203 | + justifyContent: 'center', | ||
204 | + alignItems: 'center', | ||
205 | + }, | ||
206 | + immediatelyUseText: { | ||
207 | + fontSize: 10*DEVICE_WIDTH_RATIO, | ||
208 | + color: '#444444', | ||
209 | + }, | ||
210 | + grayImage: { | ||
211 | + position: 'absolute', | ||
212 | + top: 21.5*DEVICE_WIDTH_RATIO, | ||
213 | + right: 10*DEVICE_WIDTH_RATIO, | ||
214 | + width: 63*DEVICE_WIDTH_RATIO, | ||
215 | + height: 57*DEVICE_WIDTH_RATIO, | ||
216 | + }, | ||
217 | + detail: { | ||
218 | + width: 355*DEVICE_WIDTH_RATIO, | ||
219 | + marginTop: -6*DEVICE_WIDTH_RATIO, | ||
220 | + backgroundColor: 'rgba(255,255,255,0.7)', | ||
221 | + paddingTop: 18*DEVICE_WIDTH_RATIO, | ||
222 | + paddingBottom: 11*DEVICE_WIDTH_RATIO, | ||
223 | + paddingHorizontal: 11*DEVICE_WIDTH_RATIO, | ||
224 | + zIndex: 0, | ||
225 | + }, | ||
226 | + detailTextView: { | ||
227 | + flexDirection: 'row', | ||
228 | + }, | ||
229 | + detailText: { | ||
230 | + fontFamily: 'PingFang-SC-Regular', | ||
231 | + fontSize: 11*DEVICE_WIDTH_RATIO, | ||
232 | + color: '#444444', | ||
233 | + letterSpacing: 0, | ||
234 | + }, | ||
235 | + shadow: { | ||
236 | + shadowColor: 'rgba(0,0,0,0.1)', | ||
237 | + shadowOffset: {width: 0, height: 3}, | ||
238 | + shadowOpacity: 0.5, | ||
239 | + shadowRadius: 5, | ||
240 | + elevation: 1, | ||
241 | + }, | ||
242 | + overDueSoon: { | ||
243 | + position: 'absolute', | ||
244 | + top: 0, | ||
245 | + right: 0, | ||
246 | + width: 42*DEVICE_WIDTH_RATIO, | ||
247 | + height: 42*DEVICE_WIDTH_RATIO, | ||
24 | } | 248 | } |
25 | }) | 249 | }) |
@@ -18,13 +18,15 @@ export default class CouponTabs extends Component { | @@ -18,13 +18,15 @@ export default class CouponTabs extends Component { | ||
18 | 18 | ||
19 | render() { | 19 | render() { |
20 | const { activeTab } = this.props; | 20 | const { activeTab } = this.props; |
21 | + let image = this.props.showFilter ? require('../../images/blackupa.png') : require('../../images/blackdwona.png'); | ||
21 | return ( | 22 | return ( |
22 | <View style={styles.tabs}> | 23 | <View style={styles.tabs}> |
23 | {this.props.tabs.map((tab, i) => { | 24 | {this.props.tabs.map((tab, i) => { |
24 | return ( | 25 | return ( |
25 | - <TouchableOpacity key={i} activeOpacity={0.8} style={styles.tab} onPress={() => this.props.goToPage(i)}> | 26 | + <TouchableOpacity key={i} activeOpacity={0.8} style={styles.tab} onPress={() => this._goToPage(i)}> |
26 | <View style={[styles.tabRow, i > 0 && styles.tabBorder]}> | 27 | <View style={[styles.tabRow, i > 0 && styles.tabBorder]}> |
27 | <Text style={[styles.tabText, activeTab == i && styles.activeTabText]}>{tab}{this.renderNums(i)}</Text> | 28 | <Text style={[styles.tabText, activeTab == i && styles.activeTabText]}>{tab}{this.renderNums(i)}</Text> |
29 | + {i == 0 && <Image source={activeTab == i ? image : require('../../images/down.png')} style={styles.image} resizeMode="stretch" />} | ||
28 | </View> | 30 | </View> |
29 | </TouchableOpacity> | 31 | </TouchableOpacity> |
30 | ) | 32 | ) |
@@ -58,6 +60,13 @@ export default class CouponTabs extends Component { | @@ -58,6 +60,13 @@ export default class CouponTabs extends Component { | ||
58 | return ` (${nums})`; | 60 | return ` (${nums})`; |
59 | } | 61 | } |
60 | } | 62 | } |
63 | + | ||
64 | + _goToPage(i) { | ||
65 | + let shouldGo = this.props.selectTab(i); | ||
66 | + if (shouldGo) { | ||
67 | + this.props.goToPage(i); | ||
68 | + } | ||
69 | + } | ||
61 | } | 70 | } |
62 | 71 | ||
63 | const styles = StyleSheet.create({ | 72 | const styles = StyleSheet.create({ |
@@ -91,7 +100,6 @@ const styles = StyleSheet.create({ | @@ -91,7 +100,6 @@ const styles = StyleSheet.create({ | ||
91 | }, | 100 | }, |
92 | tabText: { | 101 | tabText: { |
93 | fontSize: 14, | 102 | fontSize: 14, |
94 | - lineHeight: 30, | ||
95 | color: '#b0b0b0', | 103 | color: '#b0b0b0', |
96 | }, | 104 | }, |
97 | activeTabText: { | 105 | activeTabText: { |
@@ -100,5 +108,10 @@ const styles = StyleSheet.create({ | @@ -100,5 +108,10 @@ const styles = StyleSheet.create({ | ||
100 | icon: { | 108 | icon: { |
101 | width: 24, | 109 | width: 24, |
102 | height: 38, | 110 | height: 38, |
111 | + }, | ||
112 | + image: { | ||
113 | + width: 9, | ||
114 | + height: 6, | ||
115 | + marginLeft: 5, | ||
103 | } | 116 | } |
104 | }); | 117 | }); |
@@ -21,5 +21,8 @@ export default keyMirror({ | @@ -21,5 +21,8 @@ export default keyMirror({ | ||
21 | 21 | ||
22 | HIDE_SUCCESS_PROMPT: null, | 22 | HIDE_SUCCESS_PROMPT: null, |
23 | HIDE_NET_ERROR_PROMPT: null, | 23 | HIDE_NET_ERROR_PROMPT: null, |
24 | + SHOW_PROMPT_TIP: null, | ||
25 | + GET_COUPONLIST_REQUEST: null, | ||
26 | + GET_COUPONLIST_FAILURE: null, | ||
24 | 27 | ||
25 | }); | 28 | }); |
@@ -13,6 +13,7 @@ import {Map} from 'immutable'; | @@ -13,6 +13,7 @@ import {Map} from 'immutable'; | ||
13 | import * as couponActions from '../reducers/coupon/couponActions'; | 13 | import * as couponActions from '../reducers/coupon/couponActions'; |
14 | import CouponList from '../components/coupon/CouponList'; | 14 | import CouponList from '../components/coupon/CouponList'; |
15 | import CouponTabs from '../components/coupon/CouponTabs'; | 15 | import CouponTabs from '../components/coupon/CouponTabs'; |
16 | +import Prompt from '../components/coupon/Prompt'; | ||
16 | 17 | ||
17 | const actions = [ | 18 | const actions = [ |
18 | couponActions, | 19 | couponActions, |
@@ -46,36 +47,103 @@ class CouponListContainer extends Component { | @@ -46,36 +47,103 @@ class CouponListContainer extends Component { | ||
46 | props.actions.getCouponList('overtime', true); | 47 | props.actions.getCouponList('overtime', true); |
47 | } | 48 | } |
48 | 49 | ||
49 | - state = {couponCode: ''} | 50 | + state = {couponCode: '', selectedTab: 0, showFilter: false, selectedFilter: 0} |
50 | 51 | ||
51 | render() { | 52 | render() { |
52 | - const { couponNums } = this.props.coupon; | 53 | + const { showFilter, selectedFilter } = this.state; |
54 | + const { couponNums, notuse, use, overtime, showSuccessTip } = this.props.coupon; | ||
53 | return ( | 55 | return ( |
54 | - <ScrollableTabView | ||
55 | - renderTabBar={() => <CouponTabs couponNums={couponNums} />} | ||
56 | - > | ||
57 | - <View style={styles.container} tabLabel="未使用"> | ||
58 | - <View style={styles.bindCouponContainer}> | ||
59 | - <TextInput | ||
60 | - style={styles.textInput} | ||
61 | - onChangeText={text => this.setState({couponCode: text})} | ||
62 | - placeholder="请输入优惠券码" | ||
63 | - underlineColorAndroid="transparent" | ||
64 | - /> | ||
65 | - <TouchableOpacity | ||
66 | - onPress={() => null} | ||
67 | - style={[styles.bindCouponBtn, this.state.couponCode.length > 0 && styles.blackBack]} | ||
68 | - > | ||
69 | - <Text style={styles.bindCouponText}>兑换</Text> | ||
70 | - </TouchableOpacity> | 56 | + <View style={styles.container}> |
57 | + <ScrollableTabView | ||
58 | + onChangeTab={this.onChangeTab} | ||
59 | + renderTabBar={() => <CouponTabs couponNums={couponNums} selectTab={this.selectTab} showFilter={this.state.showFilter}/>} | ||
60 | + > | ||
61 | + <View style={styles.container} tabLabel="未使用"> | ||
62 | + <View style={styles.bindCouponContainer}> | ||
63 | + <TextInput | ||
64 | + style={styles.textInput} | ||
65 | + onChangeText={text => this.setState({couponCode: text})} | ||
66 | + placeholder="请输入优惠券码" | ||
67 | + underlineColorAndroid="transparent" | ||
68 | + /> | ||
69 | + <TouchableOpacity | ||
70 | + onPress={this.bindCoupon} | ||
71 | + style={[styles.bindCouponBtn, this.state.couponCode.length > 0 && styles.blackBack]} | ||
72 | + > | ||
73 | + <Text style={styles.bindCouponText}>兑换</Text> | ||
74 | + </TouchableOpacity> | ||
75 | + </View> | ||
76 | + <CouponList isFetching={notuse.isFetching} data={notuse.list} onEndReached={() => this.props.actions.getCouponList('notuse')} type="notuse"/> | ||
71 | </View> | 77 | </View> |
72 | - <CouponList /> | ||
73 | - </View> | ||
74 | - <View style={{flex: 1, backgroundColor: 'yellow'}} tabLabel="已使用"/> | ||
75 | - <View style={{flex: 1, backgroundColor: 'green'}} tabLabel="已失效"/> | ||
76 | - </ScrollableTabView> | 78 | + <View style={styles.container} tabLabel="已使用"> |
79 | + <CouponList isFetching={use.isFetching} data={use.list} onEndReached={() => this.props.actions.getCouponList('use')} type="use"/> | ||
80 | + </View> | ||
81 | + <View style={styles.container} tabLabel="已失效"> | ||
82 | + <CouponList isFetching={overtime.isFetching} data={overtime.list} onEndReached={() => this.props.actions.getCouponList('overtime')} type="overtime"/> | ||
83 | + </View> | ||
84 | + </ScrollableTabView> | ||
85 | + {showSuccessTip ? <Prompt | ||
86 | + text={showSuccessTip} | ||
87 | + duration={800} | ||
88 | + onPromptHidden={this._onPromptHidden} | ||
89 | + /> : null} | ||
90 | + {showFilter ? <View style={styles.filterContianer}> | ||
91 | + <View style={styles.filterTabs}> | ||
92 | + {notuse.filters && notuse.filters.map((item, index) => { | ||
93 | + return ( | ||
94 | + <TouchableOpacity | ||
95 | + activeOpacity={0.9} | ||
96 | + key={index} | ||
97 | + style={[styles.filterTab, (selectedFilter == item.filter_id) && styles.selectedFilterTab]} | ||
98 | + onPress={() => this.pressFilter(item.filter_id)}> | ||
99 | + <Text style={[styles.filterText, (selectedFilter == item.filter_id) && styles.selectedFilterText]}>{item.filter_name}</Text> | ||
100 | + </TouchableOpacity> | ||
101 | + ) | ||
102 | + })} | ||
103 | + </View> | ||
104 | + <TouchableOpacity style={styles.container} onPress={() => this.setState({showFilter: false})} /> | ||
105 | + </View> : null} | ||
106 | + </View> | ||
77 | ) | 107 | ) |
78 | } | 108 | } |
109 | + | ||
110 | + selectTab = i => { | ||
111 | + if (i == 0 && this.state.selectedTab == 0) { | ||
112 | + this.setState({showFilter: !this.state.showFilter}); | ||
113 | + return false; | ||
114 | + } else { | ||
115 | + return true; | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + onChangeTab = tab => { | ||
120 | + const { i } = tab; | ||
121 | + let { showFilter } = this.state; | ||
122 | + if (i != 0) { | ||
123 | + showFilter = false; | ||
124 | + } | ||
125 | + this.setState({selectedTab: i, showFilter}); | ||
126 | + } | ||
127 | + | ||
128 | + bindCoupon = () => { | ||
129 | + this.props.actions.bindCoupon(this.state.couponCode); | ||
130 | + } | ||
131 | + | ||
132 | + pressFilter(filter_id) { | ||
133 | + if (this.state.selectedFilter == filter_id) { | ||
134 | + return | ||
135 | + } | ||
136 | + this.props.actions.getCouponList('notuse', true, filter_id); | ||
137 | + this.setState({selectedFilter: filter_id, showFilter: false}); | ||
138 | + } | ||
139 | + | ||
140 | + _onPromptHidden = () => { | ||
141 | + this.props.actions.promptHidden(); | ||
142 | + } | ||
143 | + | ||
144 | + _onNetPromptHidden() { | ||
145 | + this.props.actions.netPromptHidden(); | ||
146 | + } | ||
79 | } | 147 | } |
80 | 148 | ||
81 | const styles = StyleSheet.create({ | 149 | const styles = StyleSheet.create({ |
@@ -112,6 +180,40 @@ const styles = StyleSheet.create({ | @@ -112,6 +180,40 @@ const styles = StyleSheet.create({ | ||
112 | bindCouponText: { | 180 | bindCouponText: { |
113 | fontSize: 14, | 181 | fontSize: 14, |
114 | color: '#ffffff', | 182 | color: '#ffffff', |
183 | + }, | ||
184 | + filterContianer: { | ||
185 | + position: 'absolute', | ||
186 | + top: 45, | ||
187 | + left: 0, | ||
188 | + right: 0, | ||
189 | + bottom: 0, | ||
190 | + backgroundColor: 'rgba(0, 0, 0, 0.4)' | ||
191 | + }, | ||
192 | + filterTabs: { | ||
193 | + height: 65, | ||
194 | + flexDirection: 'row', | ||
195 | + justifyContent: 'space-around', | ||
196 | + alignItems: 'center', | ||
197 | + backgroundColor: '#ffffff' | ||
198 | + }, | ||
199 | + filterTab: { | ||
200 | + width: 73, | ||
201 | + height: 33, | ||
202 | + borderWidth: 1, | ||
203 | + borderColor: '#e0e0e0', | ||
204 | + borderRadius: 2, | ||
205 | + justifyContent: 'center', | ||
206 | + alignItems: 'center', | ||
207 | + }, | ||
208 | + selectedFilterTab: { | ||
209 | + backgroundColor: '#444444' | ||
210 | + }, | ||
211 | + filterText: { | ||
212 | + fontSize: 14, | ||
213 | + color: '#444444', | ||
214 | + }, | ||
215 | + selectedFilterText: { | ||
216 | + color: '#ffffff', | ||
115 | } | 217 | } |
116 | }) | 218 | }) |
117 | 219 |
js/coupon/images/bgblack@3x.png
0 → 100644

7.26 KB
js/coupon/images/bggrey@3x.png
0 → 100644

6.96 KB
js/coupon/images/bgred@3x.png
0 → 100644

7.25 KB
js/coupon/images/bgyellow@3x.png
0 → 100644

7.3 KB
js/coupon/images/blackdwona@3x.png
0 → 100644

15.8 KB
js/coupon/images/blackupa@3x.png
0 → 100644

15.7 KB
js/coupon/images/coupon.png
0 → 100644

8.49 KB
js/coupon/images/down@3x.png
0 → 100644

447 Bytes
js/coupon/images/guoqi@3x.png
0 → 100644

18.7 KB
js/coupon/images/tip@3x.png
0 → 100644

7.14 KB
js/coupon/images/up@3x.png
0 → 100644

497 Bytes
js/coupon/images/yishiyong@3x.png
0 → 100644

18.5 KB
@@ -18,6 +18,9 @@ const { | @@ -18,6 +18,9 @@ const { | ||
18 | HIDE_NET_ERROR_PROMPT, | 18 | HIDE_NET_ERROR_PROMPT, |
19 | GET_COUPONLIST_SUCCESS, | 19 | GET_COUPONLIST_SUCCESS, |
20 | GET_COUPONNUMS_SUCCESS, | 20 | GET_COUPONNUMS_SUCCESS, |
21 | + SHOW_PROMPT_TIP, | ||
22 | + GET_COUPONLIST_REQUEST, | ||
23 | + GET_COUPONLIST_FAILURE, | ||
21 | } = require('../../constants/actionTypes').default; | 24 | } = require('../../constants/actionTypes').default; |
22 | 25 | ||
23 | export function setContentCode(code) { | 26 | export function setContentCode(code) { |
@@ -377,26 +380,52 @@ export function getCouponListSuccess(payload) { | @@ -377,26 +380,52 @@ export function getCouponListSuccess(payload) { | ||
377 | } | 380 | } |
378 | } | 381 | } |
379 | 382 | ||
380 | -export function getCouponList(type, init) { | 383 | +export function getCouponListRequest(payload) { |
384 | + return { | ||
385 | + type: GET_COUPONLIST_REQUEST, | ||
386 | + payload, | ||
387 | + } | ||
388 | +} | ||
389 | + | ||
390 | +export function getCouponListFailure(payload) { | ||
391 | + return { | ||
392 | + type: GET_COUPONLIST_FAILURE, | ||
393 | + payload, | ||
394 | + } | ||
395 | +} | ||
396 | + | ||
397 | +export function getCouponList(type, init, filter) { | ||
381 | return (dispatch, getState) => { | 398 | return (dispatch, getState) => { |
382 | let {app, coupon} = getState(); | 399 | let {app, coupon} = getState(); |
383 | let couponData = (coupon.toJS())[type] || {}; | 400 | let couponData = (coupon.toJS())[type] || {}; |
401 | + if (!init && (couponData.isFetching || couponData.reachedEnd)) return | ||
402 | + dispatch(getCouponListRequest(type)) | ||
384 | if (init) { | 403 | if (init) { |
385 | couponData.page = 1; | 404 | couponData.page = 1; |
386 | } else { | 405 | } else { |
387 | couponData.page += 1; | 406 | couponData.page += 1; |
388 | } | 407 | } |
408 | + if (filter != null && filter != undefined) { | ||
409 | + couponData.filter = filter; | ||
410 | + } | ||
389 | return new CouponService(app.host).getCouponList(couponData) | 411 | return new CouponService(app.host).getCouponList(couponData) |
390 | .then(data => { | 412 | .then(data => { |
413 | + let length = couponData.list.length; | ||
391 | if (init) { | 414 | if (init) { |
392 | couponData.list = data.couponList; | 415 | couponData.list = data.couponList; |
393 | } else { | 416 | } else { |
394 | couponData.list = [...couponData.list, ...data.couponList]; | 417 | couponData.list = [...couponData.list, ...data.couponList]; |
395 | } | 418 | } |
419 | + if (couponData.list.length == length) { | ||
420 | + couponData.reachedEnd = true; | ||
421 | + couponData.page -= 1; | ||
422 | + } | ||
423 | + couponData.filters = data.filters; | ||
424 | + couponData.isFetching = false; | ||
396 | dispatch(getCouponListSuccess(couponData)); | 425 | dispatch(getCouponListSuccess(couponData)); |
397 | }) | 426 | }) |
398 | .catch(e => { | 427 | .catch(e => { |
399 | - dispatch(); | 428 | + dispatch(getCouponListFailure(e)); |
400 | }) | 429 | }) |
401 | } | 430 | } |
402 | } | 431 | } |
@@ -416,7 +445,28 @@ export function getCouponNums() { | @@ -416,7 +445,28 @@ export function getCouponNums() { | ||
416 | dispatch(getCouponNumsSuccess(data)); | 445 | dispatch(getCouponNumsSuccess(data)); |
417 | }) | 446 | }) |
418 | .catch(e => { | 447 | .catch(e => { |
448 | + dispatch(getCouponListFailure(e)); | ||
449 | + }) | ||
450 | + } | ||
451 | +} | ||
419 | 452 | ||
453 | +export function bindCoupon(coupon_code) { | ||
454 | + return (dispatch, getState) => { | ||
455 | + let {app} = getState(); | ||
456 | + return new CouponService(app.host).bindCoupon({coupon_code}) | ||
457 | + .then(data => { | ||
458 | + dispatch(showPrompt(data.message || '兑换成功')); | ||
459 | + dispatch(getCouponList('notuse', true)); | ||
460 | + }) | ||
461 | + .catch(e => { | ||
462 | + dispatch(showPrompt(e.message || '兑换失败')); | ||
420 | }) | 463 | }) |
421 | } | 464 | } |
465 | +} | ||
466 | + | ||
467 | +export function showPrompt(payload) { | ||
468 | + return { | ||
469 | + type: SHOW_PROMPT_TIP, | ||
470 | + payload, | ||
471 | + } | ||
422 | } | 472 | } |
@@ -10,27 +10,33 @@ let InitialState = Record({ | @@ -10,27 +10,33 @@ let InitialState = Record({ | ||
10 | floors: List(), | 10 | floors: List(), |
11 | showSuccessTip: false, | 11 | showSuccessTip: false, |
12 | showNetErrorTip: false, | 12 | showNetErrorTip: false, |
13 | - notuse: { | 13 | + notuse: new (Record({ |
14 | type: 'notuse', | 14 | type: 'notuse', |
15 | - list: List(), | 15 | + isFetching: false, |
16 | + reachedEnd: false, | ||
17 | + list: [], | ||
16 | filter: 0, | 18 | filter: 0, |
17 | page: 0, | 19 | page: 0, |
18 | limit: 10, | 20 | limit: 10, |
19 | - }, | ||
20 | - use: { | 21 | + })), |
22 | + use: new (Record({ | ||
21 | type: 'use', | 23 | type: 'use', |
22 | - list: List(), | 24 | + isFetching: false, |
25 | + reachedEnd: false, | ||
26 | + list: [], | ||
23 | filter: 0, | 27 | filter: 0, |
24 | page: 0, | 28 | page: 0, |
25 | limit: 10, | 29 | limit: 10, |
26 | - }, | ||
27 | - overtime: { | 30 | + })), |
31 | + overtime: new (Record({ | ||
28 | type: 'overtime', | 32 | type: 'overtime', |
29 | - list: List(), | 33 | + isFetching: false, |
34 | + reachedEnd: false, | ||
35 | + list: [], | ||
30 | filter: 0, | 36 | filter: 0, |
31 | page: 0, | 37 | page: 0, |
32 | limit: 10, | 38 | limit: 10, |
33 | - }, | 39 | + })), |
34 | couponNums: null, | 40 | couponNums: null, |
35 | }); | 41 | }); |
36 | 42 |
@@ -17,6 +17,9 @@ const { | @@ -17,6 +17,9 @@ const { | ||
17 | HIDE_NET_ERROR_PROMPT, | 17 | HIDE_NET_ERROR_PROMPT, |
18 | GET_COUPONLIST_SUCCESS, | 18 | GET_COUPONLIST_SUCCESS, |
19 | GET_COUPONNUMS_SUCCESS, | 19 | GET_COUPONNUMS_SUCCESS, |
20 | + SHOW_PROMPT_TIP, | ||
21 | + GET_COUPONLIST_REQUEST, | ||
22 | + GET_COUPONLIST_FAILURE, | ||
20 | } = require('../../constants/actionTypes').default; | 23 | } = require('../../constants/actionTypes').default; |
21 | 24 | ||
22 | const initialState = new InitialState; | 25 | const initialState = new InitialState; |
@@ -78,12 +81,22 @@ export default function couponReducer(state=initialState, action) { | @@ -78,12 +81,22 @@ export default function couponReducer(state=initialState, action) { | ||
78 | 81 | ||
79 | case GET_COUPONLIST_SUCCESS: { | 82 | case GET_COUPONLIST_SUCCESS: { |
80 | let data = action.payload || {}; | 83 | let data = action.payload || {}; |
81 | - return state.set(data.type, data); | 84 | + return state.set(data.type, new (Immutable.Record(data))); |
82 | } | 85 | } |
83 | 86 | ||
84 | case GET_COUPONNUMS_SUCCESS: { | 87 | case GET_COUPONNUMS_SUCCESS: { |
85 | return state.set('couponNums', action.payload); | 88 | return state.set('couponNums', action.payload); |
86 | } | 89 | } |
90 | + | ||
91 | + case SHOW_PROMPT_TIP: { | ||
92 | + return state.set('showSuccessTip', action.payload); | ||
93 | + } | ||
94 | + case GET_COUPONLIST_REQUEST: { | ||
95 | + return state.setIn([action.payload, 'isFetching'], true); | ||
96 | + } | ||
97 | + case GET_COUPONLIST_FAILURE: { | ||
98 | + return state.set('error', action.payload); | ||
99 | + } | ||
87 | } | 100 | } |
88 | 101 | ||
89 | return state; | 102 | return state; |
@@ -67,6 +67,7 @@ export default class CouponService { | @@ -67,6 +67,7 @@ export default class CouponService { | ||
67 | async getCouponList(data) { | 67 | async getCouponList(data) { |
68 | let params = {...data}; | 68 | let params = {...data}; |
69 | delete params.list; | 69 | delete params.list; |
70 | + delete params.filters; | ||
70 | return await this.api.get({ | 71 | return await this.api.get({ |
71 | url: '', | 72 | url: '', |
72 | body: { | 73 | body: { |
-
Please register or login to post a comment