Commit a29dd89c by chenxu

点单系统

parent 0075aaf1
[1207/092449.724:ERROR:directory_reader_win.cc(43)] FindFirstFile: 系统找不到指定的路径。 (0x3)
import React from 'react';
import {
View,
Text,
Dimensions,
FlatList,
Image,
Animated,
Easing,
} from 'react-native';
import Touch from './Touch';
import Icon from 'react-native-vector-icons/Ionicons';
import {setSpText} from '../utils/screen';
const {width, height} = Dimensions.get('screen');
class Car extends React.Component {
state = {
filterValue: new Animated.Value(0),
};
componentWillReceiveProps(nextProps) {
this.show(nextProps.carVisible);
}
show = carVisible => {
let {filterValue} = this.state;
Animated.timing(filterValue, {
toValue: carVisible ? 1 : 0,
duration: 800,
easing: Easing.inOut(Easing.exp),
}).start();
};
// 生成不重复的key
keyExtractor = (item: any, index: number) => {
return String(index);
};
renderHeader = () => {
return (
<View style={{flex: 1, flexDirection: 'column'}}>
<View
style={{
backgroundColor: '#f3f4f5',
paddingTop: 15,
paddingBottom: 15,
}}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}}>
<View style={styles.goodsName}>
<Text style={styles.ThText}>商品名称</Text>
</View>
<View style={styles.goodsPrice}>
<Text style={styles.ThText}>单价</Text>
</View>
<View style={styles.goodsNum}>
<Text style={styles.ThText}>数量</Text>
</View>
<View style={styles.goodsTotal}>
<Text style={styles.ThText}>金额</Text>
</View>
</View>
</View>
<View />
</View>
);
};
renderCell = (item, index) => (
<View style={index % 2 === 0 ? styles.oddItems : styles.evenItem}>
<View style={styles.goodsName}>
<Text style={styles.ItemText}>{item.name}</Text>
</View>
<View style={styles.goodsPrice}>
<Text style={styles.ItemText}>{item.prePrice}</Text>
</View>
<View style={styles.goodsNum}>
<Touch style={styles.goodsSub} onPress={() => this.sub(item)}>
<Image
style={styles.img}
source={require('../assets/Order/subtract.png')}
/>
</Touch>
<View style={styles.goodsNums}>
<Text style={styles.ItemText}>{item.num}</Text>
</View>
<Touch style={styles.goodsAdd} onPress={() => this.add(item)}>
<Image
style={styles.img}
source={require('../assets/Order/add.png')}
/>
</Touch>
</View>
<View style={styles.goodsTotal}>
<Text style={styles.ItemText}>
{(item.prePrice * item.num).toFixed(2)}
</Text>
</View>
</View>
);
empty = () => (
<View style={styles.empty}>
<View style={styles.emptyImg}>
<Image style={styles.img} source={require('../assets/Order/car.png')} />
<Text
style={{
textAlign: 'center',
color: '#ccc',
fontSize: setSpText(40),
marginTop: 30,
}}>
购物车空空如也~
</Text>
</View>
</View>
);
add = v => {
let {goodsAdd} = this.props;
goodsAdd(v);
};
sub = v => {
let {goodsSub} = this.props;
goodsSub(v);
};
render() {
let {goodsMap, goBack, num, total, emptyCar} = this.props;
let {filterValue} = this.state;
return (
<Animated.View
style={[
styles.body,
{
left: filterValue.interpolate({
inputRange: [0, 1],
outputRange: [-width, 1],
}),
},
]}>
<View>
<FlatList
style={styles.carList}
ListHeaderComponent={this.renderHeader}
keyExtractor={this.keyExtractor}
horizontal={false}
data={goodsMap}
renderItem={({item, index}) => this.renderCell(item, index)}
ListEmptyComponent={() => this.empty()}
/>
<View style={styles.footer}>
<Touch style={styles.back} onPress={() => goBack()}>
<View>
<Icon name={'md-arrow-back'} size={30} color={'#fff'} />
<Text style={{color: '#fff'}}>隐藏</Text>
</View>
</Touch>
<View style={styles.aggregate}>
<Text>共计 {num} </Text>
</View>
<View style={styles.aggregate}>
<Text>总金额:{total.toFixed(2)}</Text>
</View>
<Touch style={styles.delete} onPress={() => emptyCar()}>
<View>
<Icon name={'md-trash'} size={30} color={'#fff'} />
<Text style={{color: '#fff'}}>清空</Text>
</View>
</Touch>
</View>
</View>
<Touch style={styles.right} onPress={() => goBack()}>
<Text />
</Touch>
</Animated.View>
);
}
}
const styles = {
display: {
display: 'none',
},
body: {
width,
height: height * 0.93,
position: 'absolute',
zIndex: 9,
flexDirection: 'row',
},
right: {
width: width * 0.3,
height: height * 0.93,
backgroundColor: 'rgba(0,0,0,0.1)',
},
carList: {
width: width * 0.7,
height: height * 0.89,
backgroundColor: '#fff',
},
goodsName: {
width: width * 0.3,
},
goodsPrice: {
width: width * 0.1,
},
goodsNum: {
width: width * 0.15,
flexDirection: 'row',
justifyContent: 'center',
},
goodsSub: {
width: 30,
height: 30,
borderRadius: 30,
backgroundColor: '#FC4000',
overflow: 'hidden',
},
goodsAdd: {
width: 30,
height: 30,
borderRadius: 30,
backgroundColor: '#FC4000',
},
img: {
width: '100%',
height: '100%',
},
goodsNums: {
width: width * 0.15 - 60,
},
goodsTotal: {
width: width * 0.15,
},
ThText: {
textAlign: 'center',
fontSize: setSpText(40),
},
ItemText: {
textAlign: 'center',
fontSize: setSpText(30),
},
oddItems: {
paddingTop: 20,
paddingBottom: 20,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#fff',
},
evenItem: {
paddingTop: 20,
paddingBottom: 20,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f3f4f5',
},
footer: {
width: width * 0.7,
height: height * 0.06,
flexDirection: 'row',
backgroundColor: '#fff',
borderTopWidth: 1,
borderTopColor: '#f3f4f5',
},
back: {
flex: 4,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ccc',
},
aggregate: {
flex: 4,
justifyContent: 'center',
alignItems: 'center',
},
delete: {
flex: 4,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'red',
},
empty: {
height: height * 0.5,
alignItems: 'center',
justifyContent: 'center',
},
emptyImg: {
width: width * 0.3,
height: width * 0.3,
},
};
export default Car;
import React from 'react';
import {Dimensions, Image, Text, View} from 'react-native';
import {imgUrl} from '../utils/config';
// import Touch from './Touch';
import {setSpText} from '../utils/screen';
const {width, height} = Dimensions.get('window');
export default class GoodsList extends React.Component {
state = {
num: 0,
};
render() {
let {goodsArr} = this.props;
let {num} = this.state;
return (
<View style={styles.goodsBox}>
{goodsArr.length > 0 &&
goodsArr.map(v => {
return (
<View style={styles.goodsItem}>
<View style={styles.goodsImg}>
<Image
style={styles.img}
source={{uri: `${imgUrl}${v.image}`}}
/>
</View>
<Text style={styles.goodsName}>{v.name}</Text>
<Text style={styles.goodsPrice}>{v.prePrice}</Text>
<View style={styles.goodsNum}>
<View style={num > 0 ? styles.goodsSub : styles.display}>
<Image
style={styles.img}
source={require('../assets/Order/subtract.png')}
/>
</View>
<Text style={num > 0 ? styles.goodsNums : styles.display}>
{num}
</Text>
<View style={styles.goodsAdd}>
<Image
style={styles.img}
source={require('../assets/Order/add.png')}
/>
</View>
</View>
</View>
);
})}
</View>
);
}
}
const styles = {
goodsBox: {
width: width * 0.85,
flexDirection: 'row',
flexWrap: 'wrap',
},
goodsItem: {
width: width * 0.85 * 0.3333,
height: height * 0.8 * 0.31,
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#ccc',
borderRightWidth: 1,
borderRightColor: '#ccc',
},
goodsImg: {
width: width * 0.85 * 0.3333 * 0.95,
height: width * 0.85 * 0.3333 * 0.8,
marginTop: 10,
},
goodsName: {
width: width * 0.85 * 0.3333 * 0.95,
height: 40,
lineHeight: 50,
fontSize: setSpText(30),
},
goodsPrice: {
width: width * 0.85 * 0.3333 * 0.9,
height: 25,
textAlign: 'right',
paddingRight: 20,
marginTop: 5,
marginBottom: 5,
color: '#e21918',
fontSize: setSpText(20),
},
goodsNum: {
width: width * 0.85 * 0.3333 * 0.9,
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
},
goodsSub: {
width: 50,
height: 50,
borderRadius: 50,
backgroundColor: '#fc4000',
},
img: {
width: '100%',
height: '100%',
},
goodsNums: {
width: 50,
height: 50,
textAlign: 'center',
fontSize: 40,
},
goodsAdd: {
width: 50,
height: 50,
borderRadius: 50,
backgroundColor: '#fc4000',
},
display: {
display: 'none',
},
};
import React from 'react';
import {View, Dimensions, Text, Image} from 'react-native';
import Touch from '../components/Touch';
import {scaleSize, setSpText} from '../utils/screen';
import {login} from '../utils/config';
import QRCode from 'react-native-qrcode-svg';
import {connect} from 'react-redux';
import delay from '../utils/delay';
import Speech from '../utils/Speech';
import Toast from './Toast';
import Smilepay from '../utils/Smilepay';
import KeyEvent from 'react-native-keyevent';
const {width, height} = Dimensions.get('screen');
const paidTime = 30; // 支付完成等待时间 30s
const wait = t => new Promise(resolve => setTimeout(resolve, t));
@connect(({admin}) => ({admin}))
class Settles extends React.Component {
state = {
faceType: 'alipay',
};
componentDidUpdate(snapshot) {
// this.listenOrder();
}
listenOrder = () => {
let {settleVisible} = this.props;
if (settleVisible) {
window.scan = this.submitBarcode;
const handleScan = this.submitBarcode;
KeyEvent.onKeyDownListener(async e => {
const {keyCode, pressedKey} = e;
if (keyCode === 66) {
// 回车事件
await handleScan();
this.barcode = '';
} else if (keyCode >= 7 && keyCode <= 16) {
this.barcode += keyCode - 7;
} else if (keyCode >= 29 && keyCode <= 54) {
this.barcode += pressedKey;
}
});
this.listen();
}
};
listen = async () => {
while (!window.socket) {
await delay(1000);
}
window.socket.on(
'counterOrder',
({type, msg, sid, payType, token, orderId}) => {
let {num, total, goodsMap} = this.props;
let totalPrice = total;
let totalCount = num;
if (type === 'waiting') {
// 客户端等待,响应购物车列表
this.token = token;
const {ids, noBarcode} = this.getGoods(goodsMap);
window.socket.emit(
'counterGoods',
sid,
JSON.stringify(ids),
totalPrice,
totalCount,
payType,
JSON.stringify(noBarcode),
);
} else if (type === 'fail') {
// 下单失败
Speech.speak(msg);
} else if (type === 'paying') {
// 下单成功,等待用户支付
let count = 0;
this.props.dispatch({
// 扫码付款步骤
type: 'app/step',
step: 2,
});
clearInterval(this.payInter); // 清除定时查询订单
this.payInter = setInterval(async () => {
count++;
if (count >= 30) {
// 一分钟,超时关闭付款
this.props.dispatch({
// 返回商品扫码步骤
type: 'app/step',
step: 1,
});
clearInterval(this.payInter);
this.setState({voiceModal: false});
}
}, 2000);
this.setState({voiceModal: true, voiceTitle: msg});
// Speech.speak(msg);
} else if (type === 'paid') {
if (orderId) {
let count = 0;
this.props.dispatch({
// 扫码付款步骤
type: 'app/step',
step: 2,
});
clearInterval(this.payInter); // 清除定时查询订单
this.payInter = setInterval(async () => {
if (!this.state.voiceModal) {
clearInterval(this.payInter);
}
count++;
const order = await this.props.dispatch({
type: 'goods/status',
orderId, // 订单号,
});
if (order.code === 1 && order.data === 1) {
this.successPay();
clearInterval(this.payInter); // 清除定时查询订单
this.isPaid = true; // 支付成功,暂停检测秤的重量
this.paidTimeout = setTimeout(() => {
this.isPaid = false;
}, paidTime * 1000);
setTimeout(() => {
this.props.dispatch({
// 返回商品扫码步骤
type: 'app/step',
step: 1,
});
this.setState({voiceModal: false});
}, 1200);
}
if (count >= 20) {
// 一分钟,超时关闭付款
this.props.dispatch({
// 返回商品扫码步骤
type: 'app/step',
step: 1,
});
clearInterval(this.payInter);
this.setState({voiceModal: false});
}
}, 3000);
} else {
this.props.dispatch({
// 购物完成步骤
type: 'app/step',
step: 3,
});
this.clearList();
this.setState({voiceModal: true, voiceTitle: msg});
Speech.speak(msg);
clearInterval(this.payInter); // 清除定时查询订单
this.isPaid = true; // 支付成功,暂停检测重力感应器的重量
this.paidTimeout = setTimeout(() => {
this.isPaid = false;
}, paidTime * 1000);
setTimeout(() => {
this.props.dispatch({
// 返回商品扫码步骤
type: 'app/step',
step: 1,
});
this.setState({voiceModal: false});
}, 1200);
}
}
},
);
};
getGoods = goodsMap => {
let ids = {};
const noBarcode = [];
goodsMap.map(({barcode, num, prePrice, name}) => {
if (barcode !== '000000') {
//非无码商品
if (!ids[barcode]) {
ids[barcode] = num;
} else {
ids[barcode] += num;
}
} else {
noBarcode.push({
name,
barcode,
price: 0,
purPrice: 0,
prePrice,
num,
weight: 0,
});
}
});
return {ids, noBarcode};
};
successPay = () => {
Toast.hide();
const msg = '付款成功,祝您生活愉快~';
Speech.speak(msg);
this.props.dispatch({
// 购物完成步骤
type: 'app/step',
step: 3,
});
let {emptyCar, closeSettle} = this.props;
closeSettle();
emptyCar();
};
alipayinfo = async () => {
const {data} = await this.props.dispatch({type: 'goods/alipayinfo'});
this.alipayInfo = await Smilepay.init(data);
if (this.alipayInfo.code === '1000')
this.alipayInfo.metaInfo = JSON.parse(this.alipayInfo.metaInfo);
return this.alipayInfo;
};
smilepay = async () => {
const {goodsMap} = this.props;
if (goodsMap.length === 0) {
this.props.speak('请先扫描商品条码,再刷脸支付', true);
return;
}
await this.alipayinfo();
if (!this.alipayInfo || !this.alipayInfo.metaInfo) {
this.props.speak('刷脸支付启动失败,请重新刷脸或扫码支付', true);
return;
}
const {data} = await this.props.dispatch({
type: 'goods/smilepay',
zimmetainfo: this.alipayInfo.metaInfo,
});
if (data.code === 1) {
const res = await Smilepay.verify(JSON.parse(data.data));
if (res.code !== '1000') {
Toast.show(res.msg);
return;
}
await this.barcodepay(res.fToken, true, null, res.fToken, res.uid);
}
};
// 付款码支付
barcodepay = async (code, facepay, openid, fToken, uid) => {
if (this.barcodeBusy) {
return true;
}
Toast.loading('正在付款,请稍后~');
const isWxpay = code.toString().match(/1[0-5]\d{16}/);
const isAlipay = fToken || code.toString().match(/^(2[5-9]|30)\d{14,22}$/);
if (!isWxpay && !isAlipay) {
return false;
}
this.barcodeBusy = true;
let successPay = false;
let {goodsMap} = this.props;
const {ids, noBarcode} = this.getGoods(goodsMap);
const res = await this.props.dispatch({
type: 'goods/barcodepay',
ids,
code,
facepay,
noBarcode,
openid,
fToken,
uid,
});
if (res.code !== 1) {
await this.setState({voiceModal: true, voiceTitle: res.msg});
Speech.speak(res.msg);
}
if (res.code === 1) {
// 付款成功
this.orderId = res.data.orderId;
if (res.data.users) {
this.barcodeUser = res.data;
await this.selectUserModal();
}
if (res.data.alipayUserId) {
// 没有匹配到支付宝用户
const voiceTitle =
'为了正常出店,首次使用支付宝付款码,请用进店微信扫描屏幕二维码';
// this.orderId = res.data.orderId;
this.alipayUserId = res.data.alipayUserId;
Speech.speak(voiceTitle);
this.setState({voiceTitle});
} else {
this.successPay();
successPay = true;
await wait(1200);
// await wait(res.data.timeout);
}
} else if (res.code === 2) {
// 等待付款
for (let i = 0; i < 20; i++) {
await wait(3000);
if (!this.state.voiceModal) {
break;
}
const status = await this.props.dispatch({
type: 'goods/status',
orderId: res.data.orderId, // 订单号,
});
if (status.code === 1 && status.data === 1) {
this.successPay();
successPay = true;
await wait(1200);
break;
}
}
} else if (res.code === 3) {
// 付款失败
await wait(res.data.timeout);
} else {
await wait(1200);
}
await this.setState({voiceModal: false});
this.barcodeBusy = false;
if (successPay) {
return 'SUCCESS';
}
return true;
};
submitBarcode = async () => {
this.errGood = null;
if (!window.auth) {
const msg = '系统未授权,无法查询商品';
this.setState({voiceModal: true, voiceTitle: msg});
Speech.speak(msg);
clearTimeout(this.noAuthTimeout);
this.noAuthTimeout = setTimeout(() => {
this.setState({voiceModal: false});
}, 3000);
return;
}
const barcode = this.barcode;
if (await this.barcodepay(barcode)) {
return;
}
window.count = window.defaultCount;
if (barcode) {
const goods = this.goodsMap[barcode];
if (!goods) {
Toast.loading('正在查询商品');
}
this.setDelay();
let {goodsArr} = this.state;
let {data} = goods
? {data: {code: 1, data: goods}}
: await this.props.dispatch({
type: 'goods/getGoods',
barcode,
});
if (!data) {
return;
}
if (data.code < 0) {
this.barcode = '';
this.speak(data.msg);
return;
}
let stopSpecial = false;
if (data.data.category === '特价') {
for (let i = 0; i < goodsArr.length; i++) {
if (goodsArr[i].name === data.data.name) {
stopSpecial = true;
break;
}
}
}
let price = Number(data.data.price || data.data.prePrice);
if (data.code === 1 && price > 0 && !stopSpecial) {
if (!data.data.num) {
data.data.numChange = true;
} // 称重计价不可修改数量
this.goodsMap[barcode] = data.data;
let dataReal = data.data;
dataReal.num = data.data.num || 1;
goodsArr.push({...dataReal});
const current = Math.ceil(goodsArr.length / window.pageSize);
this.setState({
current,
goodsArr,
modalS: false,
});
Toast.hide();
} else if (data.code === 1 && price <= 0) {
Toast.info('商品价格异常,请联系客服人员处理', 2);
} else if (data.code === 1 && stopSpecial) {
Toast.info('特价商品每人限购一次哦', 2);
} else if (data.code === 2) {
Toast.fail(data.data, 1.5);
}
}
};
render() {
let {settleVisible, closeSettle, num, total, admin} = this.props;
const facepay =
this.state.faceType === 'wxpay' ? this.wxFacepay : this.smilepay;
return (
<View style={settleVisible ? styles.body : styles.display}>
<View style={styles.settle}>
<View style={styles.settleBox}>
<Text style={styles.close} onPress={() => closeSettle()}>
×
</Text>
<Text style={styles.total}>
<Text style={{color: 'red', fontSize: setSpText(50)}}>
{' '}
{num}{' '}
</Text>
,共计
<Text style={{color: 'red', fontSize: setSpText(50)}}>
{' '}
{total.toFixed(2)}{' '}
</Text>
</Text>
<Text style={styles.title}>请使用微信或支付宝扫码付款</Text>
<View style={styles.payCode}>
<QRCode
logoSize={width > 801 ? 60 : scaleSize(70)}
// logoBackgroundColor="#fc4000"
logo={require('../assets/logo2.png')}
size={height * 0.15}
value={`${login}/#/counter-order-${admin.socket}`}
/>
</View>
<Text style={styles.title}>刷脸支付或使用付款码</Text>
<Touch style={styles.choose} onPress={facepay}>
<View style={styles.chooseItem}>
<View style={styles.payImg}>
<Image
style={styles.img}
source={require('../assets/Vertical/face.png')}
/>
</View>
<Text style={styles.payText}>刷脸支付</Text>
</View>
</Touch>
<Touch style={styles.choose} onPress={this.payCode}>
<View style={styles.chooseItem}>
<View style={styles.payImg}>
<Image
style={styles.img}
source={require('../assets/Vertical/payCode.png')}
/>
</View>
<Text style={styles.payText}>付款码支付</Text>
</View>
</Touch>
</View>
</View>
</View>
);
}
}
const styles = {
body: {},
display: {
display: 'none',
},
settle: {
width,
height,
backgroundColor: 'rgba(0,0,0,0.3)',
position: 'absolute',
zIndex: 9,
alignItems: 'center',
justifyContent: 'center',
},
payCode: {
width: height * 0.15,
height: height * 0.15,
},
settleBox: {
position: 'relative',
width: width * 0.7,
height: height * 0.6,
backgroundColor: '#fff',
alignItems: 'center',
},
close: {
position: 'absolute',
top: 20,
right: 10,
width: 50,
height: 50,
fontSize: setSpText(80),
textAlign: 'center',
lineHeight: 50,
},
total: {
height: height * 0.6 * 0.15,
lineHeight: height * 0.6 * 0.2,
fontSize: setSpText(30),
},
title: {
height: height * 0.6 * 0.12,
lineHeight: height * 0.6 * 0.12,
fontSize: setSpText(30),
},
choose: {
width: width * 0.7,
height: height * 0.6 * 0.15,
marginBottom: 20,
},
chooseItem: {
width: width * 0.7,
height: height * 0.6 * 0.15,
backgroundColor: '#f3f4f5',
flexDirection: 'row',
alignItems: 'center',
},
payImg: {
width: height * 0.6 * 0.1,
height: height * 0.6 * 0.1,
marginLeft: 50,
borderRadius: 10,
},
img: {
width: '100%',
height: '100%',
},
payText: {
fontSize: setSpText(50),
marginLeft: 20,
},
};
export default Settles;
import React from 'react';
import {
View,
Dimensions,
Image,
Text,
TextInput,
ScrollView,
} from 'react-native';
import {setSpText} from '../utils/screen';
import {connect} from 'react-redux';
import {imgUrl} from '../utils/config';
import Touch from './Touch';
// import GoodsList from './GoodsList';
const {width, height} = Dimensions.get('screen');
@connect(({searchGoods}) => ({searchGoods}))
class SearchGoods extends React.Component {
state = {
goodsArr: [],
// value:'',
};
search = async text => {
let {data} = await this.props.dispatch({
type: 'searchGoods/getStoreGoods',
keyword: text,
page: 0,
size: 10,
});
this.setState({
goodsArr: data.data,
});
};
render() {
let {
searchVisible,
cancelSearch,
goodsNumber,
goodsAdd,
goodsSub,
} = this.props;
let {goodsArr} = this.state;
return (
<View style={searchVisible ? styles.body : styles.display}>
<View style={styles.search}>
<View>
<View style={styles.searchInput}>
<View style={styles.searchIcon}>
<Image
style={styles.img}
source={require('../assets/Order/search.png')}
/>
</View>
<View style={styles.searchText}>
<TextInput
style={styles.input}
onChange={({nativeEvent: {text}}) => this.search(text)}
/>
</View>
<Text
style={styles.searchBtn}
onPress={() => {
cancelSearch(), this.setState({goodsArr: []});
}}>
取消
</Text>
</View>
</View>
</View>
<ScrollView style={styles.searchList}>
<View style={styles.goodsBox}>
{goodsArr.length > 0 &&
goodsArr.map((v, key) => {
return (
<View style={styles.goodsItem} key={key}>
<View style={styles.goodsImg}>
<Image
style={styles.img}
source={{uri: `${imgUrl}${v.image}`}}
/>
</View>
<Text style={styles.goodsName}>{v.name}</Text>
<Text style={styles.goodsPrice}>{v.prePrice}</Text>
<View style={styles.goodsNum}>
<Touch
style={
goodsNumber[v.barcode] &&
goodsNumber[v.barcode].num > 0
? styles.goodsSub
: styles.display
}
onPress={() => {
goodsSub(v);
}}>
<Image
style={styles.img}
source={require('../assets/Order/subtract.png')}
/>
</Touch>
<Text
style={
goodsNumber[v.barcode] &&
goodsNumber[v.barcode].num > 0
? styles.goodsNums
: styles.display
}>
{goodsNumber[v.barcode]
? goodsNumber[v.barcode].num
: ''}
</Text>
<Touch
style={styles.goodsAdd}
onPress={() => {
goodsAdd(v);
}}>
<Image
style={styles.img}
source={require('../assets/Order/add.png')}
/>
</Touch>
</View>
</View>
);
})}
</View>
</ScrollView>
</View>
);
}
}
const styles = {
body: {
width,
height,
position: 'absolute',
zIndex: 8,
backgroundColor: '#fff',
},
display: {
display: 'none',
},
search: {
width,
height: height * 0.07,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
searchInput: {
width: width * 0.9,
height: height * 0.07 * 0.85,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: height * 0.07 * 0.85,
flexDirection: 'row',
alignItems: 'center',
},
input: {
width: '100%',
height: '100%',
fontSize: setSpText(50),
},
searchIcon: {
width: width * 0.08,
height: width * 0.08,
marginLeft: 20,
},
searchText: {
width: width * 0.63,
height: height * 0.07 * 0.85,
},
searchBtn: {
width: width * 0.15,
height: height * 0.07 * 0.7,
borderRadius: height * 0.07 * 0.7,
fontSize: setSpText(50),
color: '#fff',
backgroundColor: '#fc4000',
textAlign: 'center',
lineHeight: height * 0.07 * 0.7,
},
img: {
width: '100%',
height: '100%',
},
searchList: {
width: width,
height: height * 0.93,
borderTopColor: '#ccc',
},
goodsBox: {
width: width,
flexDirection: 'row',
flexWrap: 'wrap',
backgroundColor: '#f3f4f5',
},
goodsItem: {
width: width * 0.3,
height: height * 0.8 * 0.3,
marginTop: 20,
marginLeft: width * 0.025,
alignItems: 'center',
backgroundColor: '#fff',
borderRadius: 20,
},
goodsImg: {
width: width * 0.85 * 0.3333 * 0.95,
height: width * 0.85 * 0.3333 * 0.8,
marginTop: 10,
},
goodsName: {
width: width * 0.85 * 0.3333 * 0.95,
height: 40,
lineHeight: 50,
fontSize: setSpText(30),
},
goodsPrice: {
width: width * 0.85 * 0.3333 * 0.9,
height: 25,
paddingRight: 20,
marginTop: 5,
marginBottom: 5,
color: '#e21918',
fontSize: setSpText(20),
},
goodsNum: {
width: width * 0.85 * 0.3333 * 0.9,
height: 40,
position: 'relative',
},
goodsSub: {
width: 40,
height: 40,
borderRadius: 40,
backgroundColor: '#fc4000',
},
goodsNums: {
width: width * 0.85 * 0.3333 * 0.9 - 100,
height: 40,
textAlign: 'center',
fontSize: 40,
position: 'absolute',
left: 50,
},
goodsAdd: {
width: 40,
height: 40,
borderRadius: 40,
backgroundColor: '#fc4000',
position: 'absolute',
right: 0,
},
};
export default SearchGoods;
......@@ -2,7 +2,15 @@ import * as api from '../services/searchGoods';
export default {
namespace: 'searchGoods',
state: {},
state: {
goodsNum: {},
},
reducers: {
goodsNum(state, {goodsNumber}) {
state.goodsNum = goodsNumber;
return {...state};
},
},
effects: {
*getStoreGoods(action, {call}) {
return yield call(api.getStoreGoods, action);
......
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
View,
Image,
Text,
Dimensions,
ScrollView,
TextInput,
} from 'react-native';
import Touch from '../components/Touch';
import {View, Image, Text, Dimensions, ScrollView} from 'react-native';
import {setSpText} from '../utils/screen';
import GoodsList from '../components/GoodsList';
import SearchGoods from '../components/searchGoods';
import Car from '../components/Car';
import Settles from '../components/Settles';
import {imgUrl} from '../utils/config';
const {width, height} = Dimensions.get('window');
const {width, height} = Dimensions.get('screen');
@connect(({searchGoods}) => ({searchGoods}))
class OrderPage extends Component {
state = {
goodsArr: [],
num: 0,
carVisible: false,
searchVisible: false,
settleVisible: false,
goodsNumber: {},
total: 0,
goodsMap: [],
pageX: 0,
pageY: 0,
};
ref = '';
componentDidMount() {
this.getGoodsList();
// window.scan = this.submitBarcode;
// const handleScan = this.submitBarcode;
// KeyEvent.removeKeyDownListener();
}
getGoodsList = async () => {
let {data} = await this.props.dispatch({
type: 'searchGoods/getStoreGoods',
page: 0,
size: 10,
size: 100,
});
if (data.code === 1) {
this.setState({
await this.setState({
goodsArr: data.data,
});
}
};
goodsCar = () => {
let {carVisible} = this.state;
this.setState({
carVisible: !carVisible,
});
};
goodsSub = async v => {
let {goodsNumber, num, total} = this.state;
goodsNumber[v.barcode].num -= 1;
if (goodsNumber[v.barcode].num === 0) {
delete goodsNumber[v.barcode];
}
this.setState({
goodsNumber,
num: num - 1,
total: total - v.prePrice,
});
this.total(goodsNumber);
};
goodsAdd = async v => {
let {goodsNumber, num, total} = this.state;
if (goodsNumber[v.barcode] && goodsNumber[v.barcode].num > 0) {
goodsNumber[v.barcode].num += 1;
} else {
goodsNumber[v.barcode] = {
num: 1,
name: v.name,
prePrice: v.prePrice,
barcode: v.barcode,
};
}
this.setState({
goodsNumber,
num: num + 1,
total: total + v.prePrice,
});
await this.total(goodsNumber);
};
total = goodsNumber => {
let goods = [];
Object.keys(goodsNumber).map(v => {
goods.push(goodsNumber[v]);
});
this.setState({
goodsMap: goods,
});
};
emptyCar = async () => {
await this.setState({
goodsNumber: {},
num: 0,
total: 0,
});
this.total(this.state.goodsNumber);
};
visible = () => {
this.setState({
searchVisible: true,
});
};
clearLister = () => {
this.setState({
settleVisible: true,
});
};
render() {
let {goodsArr} = this.state;
let {
goodsArr,
num,
carVisible,
searchVisible,
settleVisible,
goodsNumber,
total,
goodsMap,
} = this.state;
return (
<View style={styles.body}>
<View style={styles.search}>
{/*<View style={styles.dynamic} />*/}
<Car
carVisible={carVisible}
num={num}
total={total}
emptyCar={this.emptyCar}
goodsMap={goodsMap}
goBack={this.goodsCar}
goodsSub={this.goodsSub}
goodsAdd={this.goodsAdd}
/>
<SearchGoods
searchVisible={searchVisible}
goodsNumber={goodsNumber}
goodsAdd={this.goodsAdd}
goodsSub={this.goodsSub}
cancelSearch={() => this.setState({searchVisible: false})}
/>
<Settles
settleVisible={settleVisible}
num={num}
total={total}
goodsMap={goodsMap}
emptyCar={this.emptyCar}
closeSettle={() => this.setState({settleVisible: false})}
/>
<Touch style={styles.search} onPress={() => this.visible()}>
<View style={styles.searchInput}>
<View style={styles.searchIcon}>
<Image
......@@ -47,29 +161,114 @@ class OrderPage extends Component {
source={require('../assets/Order/search.png')}
/>
</View>
<View style={styles.searchText}>
<TextInput style={styles.input} />
</View>
<View style={styles.searchText} />
<Text style={styles.searchBtn}>搜索</Text>
</View>
</View>
</Touch>
<View style={styles.info}>
<View style={styles.classify} />
{/*分类*/}
{/*<View style={styles.classify} />*/}
{/*列表*/}
<ScrollView style={styles.goods}>
<GoodsList goodsArr={goodsArr} />
<View style={styles.goodsBox}>
{goodsArr.length > 0 &&
goodsArr.map((v, key) => {
return (
<View style={styles.goodsItem} key={key}>
<View style={styles.goodsImg}>
<Image
style={styles.img}
source={{uri: `${imgUrl}${v.image}`}}
/>
</View>
<Text style={styles.goodsName}>{v.name}</Text>
<Text style={styles.goodsPrice}>{v.prePrice}</Text>
<View style={styles.goodsNum}>
<Touch
style={
goodsNumber[v.barcode] &&
goodsNumber[v.barcode].num > 0
? styles.goodsSub
: styles.display
}
onPress={() => {
this.goodsSub(v);
}}>
<Image
style={styles.img}
source={require('../assets/Order/subtract.png')}
/>
</Touch>
<Text
style={
goodsNumber[v.barcode] &&
goodsNumber[v.barcode].num > 0
? styles.goodsNums
: styles.display
}>
{goodsNumber[v.barcode]
? goodsNumber[v.barcode].num
: ''}
</Text>
<Touch
style={styles.goodsAdd}
// ref={ref => (v.ref = ref)}
onPress={() => {
this.goodsAdd(v);
}}>
<Image
style={styles.img}
source={require('../assets/Order/add.png')}
/>
</Touch>
</View>
</View>
);
})}
</View>
</ScrollView>
</View>
<View style={styles.settle}>
<View style={styles.car}>
<Touch
style={num > 0 ? styles.carImgs : styles.carImg}
onPress={this.goodsCar}>
<View>
<View style={[num > 0 ? styles.carNum : styles.display]}>
<Text style={{color: '#fff'}}>{num}</Text>
</View>
<Image
style={styles.img}
source={require('../assets/Order/car.png')}
/>
</View>
</Touch>
</View>
<View style={styles.total}>
<Text style={{marginLeft: 130, fontSize: setSpText(50)}}>
总计:
<Text style={{fontSize: setSpText(40), color: '#e21918'}}>
99.<Text style={{fontSize: setSpText(25)}}>00</Text>
{num > 0 ? (
<Text style={{marginLeft: 30, fontSize: setSpText(50)}}>
总计:
<Text style={{fontSize: setSpText(40), color: '#e21918'}}>
{parseInt(total)}
<Text style={{fontSize: setSpText(25)}}>
.
{parseInt((total - parseInt(total)).toFixed(2) * 100) ||
'00'}
</Text>
</Text>
</Text>
</Text>
) : (
<Text style={{marginLeft: 30, fontSize: setSpText(50)}}>
暂无商品
</Text>
)}
</View>
<View style={styles.onSubmit}>
<Text style={{color: '#fff', fontSize: setSpText(50)}}>结算</Text>
<View style={num > 0 ? styles.onSubmit : styles.noSubmit}>
<Text
style={{color: '#fff', fontSize: setSpText(50)}}
onPress={() => num > 0 && this.clearLister()}>
结算
</Text>
</View>
</View>
</View>
......@@ -83,6 +282,18 @@ const styles = {
height,
backgroundColor: '#fff',
},
hidden: {
display: 'none',
},
dynamic: {
width: 20,
height: 20,
borderRadius: 20,
backgroundColor: 'red',
zIndex: 9,
position: 'absolute',
},
search: {
width,
height: height * 0.07,
......@@ -105,16 +316,16 @@ const styles = {
fontSize: setSpText(50),
},
searchIcon: {
width: height * 0.07 * 0.85,
height: height * 0.07 * 0.85,
width: width * 0.08,
height: width * 0.08,
marginLeft: 20,
},
searchText: {
width: height * 0.07 * 5.1,
width: width * 0.63,
height: height * 0.07 * 0.85,
},
searchBtn: {
width: height * 0.07 * 1.2,
width: width * 0.15,
height: height * 0.07 * 0.7,
borderRadius: height * 0.07 * 0.7,
fontSize: setSpText(50),
......@@ -126,37 +337,36 @@ const styles = {
info: {
width,
height: height * 0.86,
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: '#ccc',
},
classify: {
width: width * 0.15,
height: height * 0.86,
width: width,
height: height * 0.06,
backgroundColor: '#ccc',
},
goods: {
width: width * 0.8,
height: height * 0.86,
width: width,
height: height * 0.8,
},
goodsBox: {
width: width * 0.85,
width: width,
flexDirection: 'row',
flexWrap: 'wrap',
backgroundColor: '#f3f4f5',
},
goodsItem: {
width: width * 0.85 * 0.3333,
height: height * 0.8 * 0.31,
width: width * 0.3,
height: height * 0.8 * 0.3,
marginTop: 20,
marginLeft: width * 0.025,
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#ccc',
borderRightWidth: 1,
borderRightColor: '#ccc',
backgroundColor: '#fff',
borderRadius: 20,
},
goodsImg: {
width: width * 0.85 * 0.3333 * 0.95,
height: width * 0.85 * 0.3333 * 0.8,
backgroundColor: 'skyblue',
marginTop: 10,
},
goodsName: {
......@@ -168,7 +378,7 @@ const styles = {
goodsPrice: {
width: width * 0.85 * 0.3333 * 0.9,
height: 25,
textAlign: 'right',
// textAlign: 'right',
paddingRight: 20,
marginTop: 5,
marginBottom: 5,
......@@ -177,43 +387,80 @@ const styles = {
},
goodsNum: {
width: width * 0.85 * 0.3333 * 0.9,
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
height: 40,
position: 'relative',
},
goodsSub: {
width: 50,
height: 50,
borderRadius: 50,
backgroundColor: 'pink',
width: 40,
height: 40,
borderRadius: 40,
backgroundColor: '#fc4000',
},
img: {
width: '100%',
height: '100%',
},
goodsNums: {
width: 50,
height: 50,
width: width * 0.85 * 0.3333 * 0.9 - 100,
height: 40,
textAlign: 'center',
fontSize: 40,
position: 'absolute',
left: 50,
},
goodsAdd: {
width: 50,
height: 50,
borderRadius: 50,
backgroundColor: 'pink',
width: 40,
height: 40,
borderRadius: 40,
backgroundColor: '#fc4000',
position: 'absolute',
right: 0,
},
settle: {
width,
height: height * 0.07,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F3F4F5',
},
car: {
width: width * 0.15,
height: height * 0.06,
position: 'relative',
},
display: {
display: 'none',
},
carNum: {
width: 20,
height: 20,
position: 'absolute',
top: 5,
right: -8,
borderRadius: 25,
backgroundColor: 'red',
color: '#fff',
zIndex: 2,
justifyContent: 'center',
alignItems: 'center',
},
carImg: {
width: height * 0.06,
height: height * 0.06,
marginLeft: 20,
backgroundColor: '#ccc',
},
carImgs: {
width: height * 0.06,
height: height * 0.06,
marginLeft: 20,
backgroundColor: '#fc4000',
},
total: {
width: width * 0.75,
width: width * 0.6,
height: height * 0.07,
justifyContent: 'center',
backgroundColor: '#F3F4F5',
},
onSubmit: {
width: width * 0.23,
......@@ -223,6 +470,14 @@ const styles = {
justifyContent: 'center',
alignItems: 'center',
},
noSubmit: {
width: width * 0.23,
height: height * 0.06,
borderRadius: height * 0.07,
backgroundColor: '#ccc',
justifyContent: 'center',
alignItems: 'center',
},
};
export default OrderPage;
......@@ -2,6 +2,5 @@ import axios from 'axios';
import qs from 'qs';
export function getStoreGoods(action) {
console.warn(action);
return axios.get(`/store/goods/takeout?${qs.stringify(action)}`);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment