Authored by 张丽霞

秒杀模块,review by 孙凯

... ... @@ -21,6 +21,7 @@ import appInitialState from './reducers/app/appInitialState';
import seckillInitialState from './reducers/seckill/seckillInitialState';
import SeckillContainer from './containers/SeckillContainer';
const queryString = require('query-string');
import {
setPlatform,
... ... @@ -46,6 +47,13 @@ export default function native(platform) {
render() {
const store = configureStore(getInitialState());
store.dispatch(setPlatform(platform));
let url = this.props.url;
if (url) {
let queryUrl = queryString.parse(url);
if (queryUrl.startTime) {
store.dispatch(setStartTime(queryUrl.startTime));
}
}
if(this.props.contentCode){
store.dispatch(setContentCode(this.props.contentCode));
}
... ...
... ... @@ -33,13 +33,19 @@ export default class ProductListView extends Component {
rowData = rowData.toJS();
let brandIconUrl = SlicedImage.getSlicedUrl(rowData.defaultImages, 152, 203, 2);
let tipState='';
let btnBgColor = '#d30018';
let btnTextcolor = 'white';
if (rowData.over) {
tipState = '已抢光';
btnBgColor = '#b2b2b2';
} else if (rowData.wait) {
if (!rowData.remindFlag) {
tipState = '提醒我';
btnBgColor = '#444444';
}else {
tipState = '取消提醒';
btnBgColor = 'white';
btnTextcolor = '#000000';
}
} else {
tipState = '去抢购';
... ... @@ -55,9 +61,9 @@ export default class ProductListView extends Component {
<View style={styles.productTitlePrice}>
<Text style={styles.productTitle}>{rowData.productName}</Text>
{rowData.wait?
<View style={styles.secKillMarketPriceContainer}>
<Text style={styles.secKillPrice}>¥{rowData.secKillPrice}</Text>
<DeleteLineText style={{top:3, left:8}} text={rowData.marketPrice} />
<View style={[styles.secKillMarketPriceContainer,{marginTop:5}]}>
<Text style={[styles.secKillPrice,{color:'#d0021b'}]}>¥{rowData.secKillPrice}</Text>
<DeleteLineText style={{top:3,left:8}} text={rowData.marketPrice} />
</View>
:null
}
... ... @@ -70,7 +76,7 @@ export default class ProductListView extends Component {
<Text style={styles.seckillBeginTimeTip}>{rowData.readableTime}开始</Text>
</View>
:<View style={styles.secKillMarketPriceContainer}>
<Text style={styles.secKillPrice}>¥{rowData.secKillPrice}</Text>
<Text style={[styles.secKillPrice,{color:'#d0021b'}]}>¥{rowData.secKillPrice}</Text>
<DeleteLineText style={{top:3, left:8}} text={rowData.marketPrice} />
</View>
... ... @@ -79,8 +85,8 @@ export default class ProductListView extends Component {
<TouchableOpacity onPress={() => {
this.props.onPressRemindBtn && this.props.onPressRemindBtn(rowData);
}}>
<View style={styles.priceClickTipViewRight}>
<Text style={{color:'white'}}>{tipState}</Text>
<View style={[styles.priceClickTipViewRight,{backgroundColor: btnBgColor}]}>
<Text style={{color:btnTextcolor}}>{tipState}</Text>
</View>
</TouchableOpacity>
... ... @@ -103,7 +109,6 @@ export default class ProductListView extends Component {
let {
resource,
} = this.props;
let backgroundWidth = width;
let backgroundHeight = 64 + 20 + resource.length * rowHeight;
... ...
... ... @@ -12,7 +12,7 @@ import {
TouchableOpacity,
} from 'react-native';
import {Map} from 'immutable';
import Immutable, {Map} from 'immutable';
import ProductListView from './ProductListView';
import TimeListView from './TimeListView';
import LoadingIndicator from '../../../common/components/LoadingIndicator';
... ... @@ -23,10 +23,13 @@ export default class Seckill extends Component {
super(props);
this.renderRow = this.renderRow.bind(this);
this.renderSectionHeader = this.renderSectionHeader.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
this.isFetching = false;
this.queryActivityInfo = Immutable.fromJS({});
}
... ... @@ -55,6 +58,7 @@ export default class Seckill extends Component {
this._toast.hide({
duration: 0,
animationEnd: () => {
this.props.onClearRemindTip && this.props.onClearRemindTip();
}
})
},
... ... @@ -64,31 +68,35 @@ export default class Seckill extends Component {
})
}
renderSectionHeader(sectionData, sectionID) {
if (this.queryActivityInfo && this.queryActivityInfo.secKillProductVoList && this.queryActivityInfo.secKillProductVoList.size > 0) {
return (
<TimeListView
resource={this.queryActivityInfo.secKillProductVoList.toArray()}
onPressTimeItem={this.props.onPressTimeItem}
diff={diff}
/>
);
} else {
return null;
}
}
renderRow(rowData, sectionID, rowID, highlightRow) {
if (Array.isArray(rowData) && rowData.length == 0) {
if (Array.isArray(rowData) && rowData.length == 0 && !this.isFetching) {
switch (rowID) {
case 'ActivityTimeList':
case 'ActivityProductList':
return (
<View style={styles.seckillEndView}>
<Text style={{fontSize:16}}>来晚啦~秒杀已结束</Text>
</View>
);
case 'ActivityProductList':
return null;
default:
}
}
if (rowID == 'ActivityTimeList') {
return (
<TimeListView
resource={rowData}
onPressTimeItem={this.props.onPressTimeItem}
diff={diff}
/>
);
} else if (rowID == 'ActivityProductList') {
if (sectionID == 'ActivityProductList') {
return(
<ProductListView
resource={rowData}
... ... @@ -110,10 +118,11 @@ export default class Seckill extends Component {
queryProductList,
addCancelUserReminderTip,
} = this.props;
this.isFetching = isFetching;
this.queryActivityInfo = queryActivityInfo;
diff = Math.round((queryActivityInfo.currentTime - Date.now()) / 1000);
let dataSource = {
ActivityTimeList: queryActivityInfo.secKillProductVoList.toArray(),
ActivityProductList: queryProductList.toArray(),
ActivityProductList: [queryProductList.toArray()],
};
return (
... ... @@ -121,7 +130,7 @@ export default class Seckill extends Component {
<ListView
contentContainerStyle={styles.contentContainer}
enableEmptySections={true}
dataSource={this.dataSource.cloneWithRows(dataSource)}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this.renderRow}
enablePullToRefresh={true}
isOnPullToRefresh={isFetching}
... ... @@ -132,6 +141,7 @@ export default class Seckill extends Component {
}
this.props.onRefresh && this.props.onRefresh();
}}
renderSectionHeader={this.renderSectionHeader}
renderFooter={()=>{
return <View style={styles.descriptionContainer}>
<Text style={styles.descriptionText}>秒杀活动说明</Text>
... ...
... ... @@ -25,6 +25,7 @@ export default class SeckillTimeListView extends Component {
rowHasChanged: (r1, r2) => r1.key != r2.key,
});
this.curFocusActivity = Immutable.fromJS({});
this.secKillProductVoList = Immutable.fromJS([]);
this.state = {
selectedIndex: 0,
tickHour: '00',
... ... @@ -48,8 +49,12 @@ export default class SeckillTimeListView extends Component {
if (offsetTime <= 0) {
this.setState({
tickHour: '00',
tickMinute: '00',
tickSecond: '00',
tickTimeOut: true,
});
this.props.onPressTimeItem && this.props.onPressTimeItem(this.curFocusActivity.toJS());
} else {
this.setState({
tickHour: hour < 0 ? '00' : (hour < 10 ? ('0' + hour) : hour),
... ... @@ -60,6 +65,24 @@ export default class SeckillTimeListView extends Component {
}
}.bind(this), 1000);
//focus
if (this.secKillProductVoList.length >= 1) {
let listLength = this.secKillProductVoList.length;
let curFocusIndex = 0;
let scrollX = 0;
this.secKillProductVoList.forEach((activityItem, i) => {
if (activityItem.get('activityId') === this.curFocusActivity.get('activityId')) {
curFocusIndex = i;
}
});
if (curFocusIndex > 1 && curFocusIndex < listLength-2) {
scrollX = (curFocusIndex - 1) * rowContainerWidth;
this.listView.scrollTo({x: scrollX, y: 0, animated: false});
}
}
}
componentWillUnmount() {
... ... @@ -68,7 +91,7 @@ export default class SeckillTimeListView extends Component {
_renderRow(rowData, sectionID, rowID) {
rowData = rowData.toJS();
let colorStyle = rowData.focus ? {color: '#ff0000'} : {color: '#b0b0b0'};
let colorStyle = rowData.focus ? {color: '#d30018'} : {color: '#3e3e3e'};
return (
<TouchableOpacity onPress={() => {
if (rowData.focus) {
... ... @@ -82,7 +105,7 @@ export default class SeckillTimeListView extends Component {
<View key={'row' + rowID} style={styles.rowContainerFocus}>
{rowData.now ?
<View key={'row' + rowID} style={styles.rowContainerFocus}>
<Text style={colorStyle}>{rowData.time} 抢购中</Text>
<Text style={[colorStyle,{fontSize: 13}]}>{rowData.time} 抢购中</Text>
{!this.state.tickTimeOut ?
<View style={styles.tickTimeContainer}>
<Text style={colorStyle}>剩余:</Text>
... ... @@ -100,13 +123,13 @@ export default class SeckillTimeListView extends Component {
</View>
: null}
{rowData.over ? <Text style={colorStyle}>{rowData.time} 已开抢</Text> : null}
{rowData.over ? <Text style={[colorStyle,{fontSize: 10}]}>{rowData.time} 已开抢</Text> : null}
{rowData.wait ?
<View key={'row' + rowID} style={styles.rowContainerFocus}>
<Text style={colorStyle}>{rowData.time} 即将开抢</Text>
<Text style={[colorStyle,{fontSize: 13}]}>{rowData.time} 即将开抢</Text>
{!this.state.tickTimeOut ?
<View style={styles.tickTimeContainer}>
<Text style={colorStyle}>距开抢:</Text>
<Text style={[colorStyle,{fontSize: 10}]}>距开抢:</Text>
<View style={styles.tickTime}>
<Text style={styles.tickTimeText}>{this.state.tickHour}</Text>
</View>
... ... @@ -122,10 +145,10 @@ export default class SeckillTimeListView extends Component {
: null}
</View>
:<View key={'row' + rowID} style={styles.rowContainer}>
<Text style={colorStyle}>{rowData.time}</Text>
{rowData.now ? <Text style={colorStyle}>抢购中</Text> : null}
{rowData.over ? <Text style={colorStyle}>已开抢</Text> : null}
{rowData.wait ? <Text style={colorStyle}>即将开抢</Text> : null}
<Text style={[colorStyle,{fontSize: 13}]}>{rowData.time}</Text>
{rowData.now ? <Text style={[colorStyle,{fontSize: 10}]}>抢购中</Text> : null}
{rowData.over ? <Text style={[colorStyle,{fontSize: 10}]}>已开抢</Text> : null}
{rowData.wait ? <Text style={[colorStyle,{fontSize: 10}]}>即将开抢</Text> : null}
</View>
}
... ... @@ -152,6 +175,7 @@ export default class SeckillTimeListView extends Component {
this.curFocusActivity = focusActivity;
}
diffTime = diff;
this.secKillProductVoList = resource;
return (
<View style={[styles.container]}>
<ListView
... ... @@ -170,8 +194,10 @@ export default class SeckillTimeListView extends Component {
let {width, height} = Dimensions.get('window');
let backgroundWidth= width;
let backgroundHeight = Math.ceil((120 / 640) * width);
let backgroundHeight = 61;
let diffTime;
let rowContainerWidth = Math.ceil((223 * width) / 750);
let rowContainerFocusWidth = Math.ceil((294 * width) / 750);
let styles = StyleSheet.create({
container: {
... ... @@ -190,7 +216,7 @@ let styles = StyleSheet.create({
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
width: Math.ceil(width / 4),
width: rowContainerWidth,
height: backgroundHeight,
backgroundColor:'white',
},
... ... @@ -198,7 +224,7 @@ let styles = StyleSheet.create({
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
width: Math.ceil(width * 0.4),
width: rowContainerFocusWidth,
height: backgroundHeight,
backgroundColor:'white',
},
... ... @@ -206,17 +232,18 @@ let styles = StyleSheet.create({
flexDirection: 'row',
},
tickTime: {
backgroundColor:'red',
backgroundColor:'#d30018',
alignItems: 'center',
justifyContent: 'center',
left:0,
marginLeft:6,
width:20,
height:20,
marginLeft:5,
width:15,
height:15,
borderRadius:3,
},
tickTimeText: {
color: 'white',
fontSize: 9,
},
bottomToolBar: {
top: 200,
... ... @@ -231,8 +258,8 @@ let styles = StyleSheet.create({
},
separator: {
width: 1.5,
top: 10,
height: backgroundHeight - 20,
backgroundColor: '#e5e5e5',
top: 11,
height: backgroundHeight - 22,
backgroundColor: '#dfe3e2',
},
});
... ...
... ... @@ -7,8 +7,7 @@ export default keyMirror({
SET_CHANNEL: null,
SET_TYPE: null,
SECKILL_SET_REMIND: null,
SECKILL_CANCEL_REMIND: null,
SET_START_TIME: null,
QUERY_ACTIVITY_REQUEST: null,
QUERY_ACTIVITY_SUCCESS: null,
... ... @@ -25,6 +24,7 @@ export default keyMirror({
ADD_CANCEL_USER_REMINDER_REQUEST: null,
ADD_CANCEL_USER_REMINDER_SUCCESS: null,
ADD_CANCEL_USER_REMINDER_FAILURE: null,
ADD_CANCEL_USER_REMINDER_CLEAR_TIP: null,
UPDATE_SECKILL_PRODUCT_VO_LIST: null,
UPDATE_SECKILL_PRODUCT_LIST: null,
... ...
... ... @@ -45,6 +45,7 @@ class SeckillContainer extends Component {
this._onPressProductItem = this._onPressProductItem.bind(this);
this._onPressRemindBtn = this._onPressRemindBtn.bind(this);
this._onRefresh = this._onRefresh.bind(this);
this._onClearRemindTip = this._onClearRemindTip.bind(this);
}
componentDidMount() {
... ... @@ -53,7 +54,6 @@ class SeckillContainer extends Component {
_onPressTimeItem(activity) {
this.props.actions.clickActivityTimeItem(activity);
console.log('PressTimeItem');
}
_onPressProductItem(product) {
... ... @@ -68,6 +68,10 @@ class SeckillContainer extends Component {
this.props.actions.refreshList();
}
_onClearRemindTip() {
this.props.actions.addCancelUserReminderClearTip();
}
render() {
let {
... ... @@ -91,6 +95,7 @@ class SeckillContainer extends Component {
onPressProductItem={this._onPressProductItem}
onPressRemindBtn={this._onPressRemindBtn}
onRefresh={this._onRefresh}
onClearRemindTip={this._onClearRemindTip}
/>
);
}
... ...
... ... @@ -7,6 +7,7 @@ const moment = require('moment');
const {
SET_HOST,
SET_START_TIME,
QUERY_ACTIVITY_REQUEST,
QUERY_ACTIVITY_SUCCESS,
QUERY_ACTIVITY_FAILURE,
... ... @@ -22,6 +23,7 @@ const {
ADD_CANCEL_USER_REMINDER_REQUEST,
ADD_CANCEL_USER_REMINDER_SUCCESS,
ADD_CANCEL_USER_REMINDER_FAILURE,
ADD_CANCEL_USER_REMINDER_CLEAR_TIP,
UPDATE_SECKILL_PRODUCT_VO_LIST,
UPDATE_SECKILL_PRODUCT_LIST,
... ... @@ -122,6 +124,12 @@ export function addCancelUserReminderFailure(message) {
}
}
export function addCancelUserReminderClearTip() {
return {
type:ADD_CANCEL_USER_REMINDER_CLEAR_TIP,
}
}
export function updateSecKillProductVoList(json) {
return {
type: UPDATE_SECKILL_PRODUCT_VO_LIST,
... ... @@ -135,18 +143,25 @@ export function updateSecKillProductList(json) {
payload: json
}
}
export function setStartTime(startTime) {
return {
type: SET_START_TIME,
payload: startTime,
};
}
/*
* 秒杀时间栏
*/
export function getSeckillQueryActivity() {
return (dispatch, getState) => {
let {app, seckill} = getState();
let {app, seckill, startTime} = getState();
dispatch(queryActivityRequest());
return new SeckillService(app.host).fetchQueryActivity()
.then(json => {
if (json.secKillProductVoList && json.secKillProductVoList.length) {
let processedTimeInfo = parseActivityTimeLsit(json);
let processedTimeInfo = parseActivityTimeLsit(json, startTime);
dispatch(queryActivitySuccess(processedTimeInfo.newQueryActivityInfo));
let focusActivity = processedTimeInfo.focusActivity;
curActivityId = focusActivity.activityId;
... ... @@ -245,7 +260,6 @@ export function addCancelUserReminder(method='app.seckill.addUserReminder', acti
dispatch(addCancelUserReminderRequest());
return new SeckillService(app.host).addCancelUserReminder(method,activityId,productSkn,uid,secKillId)
.then(json => {
console.log(json);
//更新提醒状态
let remindFlag;
if (method === 'app.seckill.addUserReminder') {
... ... @@ -255,22 +269,16 @@ export function addCancelUserReminder(method='app.seckill.addUserReminder', acti
}
queryProductList = queryProductList.toJS();
let curActivity = {};
console.log('99999999');
console.log(queryProductList);
queryProductList.forEach((productItem, i) => {
if (productItem.id === secKillId) {
productItem.remindFlag = remindFlag;
}
});
console.log('5555555');
console.log(queryProductList);
dispatch(updateSecKillProductList(queryProductList));
dispatch(addCancelUserReminderSuccess(okTip));
})
.catch(error => {
console.log('error');
console.log(error);
dispatch(addCancelUserReminderFailure(failTip));
});
};
... ... @@ -319,8 +327,6 @@ export function clickRemindBtn(product) {
if (!product.secKillSku || product.secKillSku.length == 0) {
return;
}
console.log('333333');
let skn = product.secKillSku[0].productSkn;
let actionName,
action,
... ... @@ -358,8 +364,6 @@ export function clickRemindBtn(product) {
})
.then(flag => { //成功
console.log('method');
console.log(method);
dispatch(addCancelUserReminder(method, product.activityId, skn,uid, product.id, okTip, failTip));
})
.catch(error => {
... ... @@ -369,12 +373,9 @@ export function clickRemindBtn(product) {
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
console.log('1111');
queryRemindList(uid);
})
.catch(error => {
console.log('22222');
console.log(error);
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
queryRemindList(uid);
... ...
... ... @@ -7,6 +7,7 @@ let InitialState = Record({
host:'http://api.yoho.cn',
isFetching: false,
error: null,
startTime: 0,
queryActivityInfo: new (Record({
currentTime: 0,
secKillProductVoList: List(),
... ...
... ... @@ -5,6 +5,7 @@ import Immutable, {Map} from 'immutable';
const {
SET_HOST,
SET_START_TIME,
QUERY_ACTIVITY_REQUEST,
QUERY_ACTIVITY_SUCCESS,
QUERY_ACTIVITY_FAILURE,
... ... @@ -20,6 +21,7 @@ const {
ADD_CANCEL_USER_REMINDER_REQUEST,
ADD_CANCEL_USER_REMINDER_SUCCESS,
ADD_CANCEL_USER_REMINDER_FAILURE,
ADD_CANCEL_USER_REMINDER_CLEAR_TIP,
UPDATE_SECKILL_PRODUCT_VO_LIST,
UPDATE_SECKILL_PRODUCT_LIST,
... ... @@ -32,6 +34,9 @@ export default function seckillReducer(state=initialState, action) {
case SET_HOST:{
return state.set('host',action.payload)
}
case SET_START_TIME: {
return state.set('startTime',action.payload)
}
case QUERY_ACTIVITY_REQUEST: {
return state.set('isFetching', true)
.set('error', null);
... ... @@ -84,7 +89,9 @@ export default function seckillReducer(state=initialState, action) {
case ADD_CANCEL_USER_REMINDER_FAILURE: {
return state.set('addCancelUserReminderTip', Immutable.fromJS(action.payload));
}
case ADD_CANCEL_USER_REMINDER_CLEAR_TIP: {
return state.set('addCancelUserReminderTip', '');
}
case UPDATE_SECKILL_PRODUCT_VO_LIST: {
return state.setIn(['queryActivityInfo', 'secKillProductVoList'], Immutable.fromJS(action.payload))
.set('error', null);
... ...