elephant-com
Version:
the general component for elephant washing shoes
580 lines (549 loc) • 15.4 kB
JavaScript
import React, { Component } from 'react';
/* eslint-disable react/no-multi-comp,no-unused-expressions,
import/no-duplicates,react/no-array-index-key, */
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
ActivityIndicator,
ScrollView,
Modal,
TextInput,
} from 'react-native';
import { Flex, Icon, Button, Popup } from 'antd-mobile';
import moment from 'moment';
import Camera from 'react-native-camera';
import _ from 'lodash';
import { ImagePreview } from './ImagePreview';
import { height as highness, width } from './utils';
export { default as Head } from './Head';
export class CodeInputBar extends Component {
constructor(props) {
super(props);
const { code = '' } = props;
this.state = { code };
}
recognized = false;
render() {
const {
autoFocus = false, placeholder = '请扫码',
onChange, onSubmit, focusClear = false,
} = this.props;
return (<View style={styles.codeInputBarWrapper}>
<View style={styles.codeInputBarBox}>
<Icon type={'\ue670'} size={15} color="#ccc" />
<TextInput
underlineColorAndroid="#fff"
returnKeyType="done"
style={styles.codeInputBar}
autoFocus={autoFocus}
value={this.state.code}
placeholder={placeholder}
onFocus={() => focusClear && this.setState({ code: '' })}
onChangeText={(value) => {
this.setState({ code: value });
onChange && onChange(value);
}}
onSubmitEditing={(event) => {
const value = event.nativeEvent.text;
onSubmit && onSubmit(value);
}}
/>
</View>
<TouchableOpacity
onPress={() => {
this.recognized = false;
Popup.show(<View transparent style={{ width: '100%', height: '100%' }}>
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}
onBarCodeRead={(e) => {
if (!this.recognized) {
this.recognized = true;
this.setState({ code: e.data });
onSubmit && onSubmit(e.data);
Popup.hide();
}
}}
>
<View style={styles.box} />
<TouchableOpacity onPress={() => Popup.hide()} style={styles.closeScaning}>
<Text>关 闭</Text>
</TouchableOpacity>
</Camera>
</View>);
}}
>
<Icon type={'\ue689'} color="#01a7eb" size={30} />
</TouchableOpacity>
</View>);
}
}
export class CodeInput extends Component {
constructor(props) {
super(props);
const { code = '' } = props;
this.state = { code };
}
recognized = false;
/* eslint-disable no-unused-expressions,react/no-multi-comp */
render() {
const {
autoFocus = false, placeholder = '请扫码',
color = '#000', iconType = '\ue67c', iconColor = '#00f',
onChange, onSubmit, focusClear = false,
} = this.props;
return (<View style={styles.codeInputWrapper}>
<TextInput
underlineColorAndroid="#fff"
returnKeyType="done"
style={{ flex: 1, color }}
placeholderTextColor={color}
autoFocus={autoFocus}
value={this.state.code}
placeholder={placeholder}
onFocus={() => focusClear && this.setState({ code: '' })}
onChangeText={(value) => {
this.setState({ code: value });
onChange && onChange(value);
}}
onSubmitEditing={(event) => {
const value = event.nativeEvent.text;
// if (/[\r,\n]/g.test(value))
onSubmit && onSubmit(value);
}}
/>
<TouchableOpacity
onPress={() => {
this.recognized = false;
Popup.show(<View transparent style={{ width: '100%', height: '100%' }}><Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}
onBarCodeRead={(e) => {
if (!this.recognized) {
this.recognized = true;
this.setState({ code: e.data });
onChange && onChange(e.data);
Popup.hide();
}
}}
>
<View style={styles.box} />
<TouchableOpacity onPress={() => Popup.hide()} style={styles.closeScaning}>
<Text>关 闭</Text>
</TouchableOpacity>
</Camera></View>);
}}
>
<Icon type={iconType} size={34} color={iconColor} />
</TouchableOpacity>
</View>);
}
}
export const ScrollViewMid = (props) => {
const { bgColor = 'rgba(0,0,0,0)', children } = props;
return (<ScrollView style={{ backgroundColor: bgColor }}>
{children}
</ScrollView>);
};
export class SwitchButton extends Component {
handler = () => {
if (this.props.onDataChange) {
this.props.onDataChange(!this.props.down);
}
};
render() {
const { bgColor = '#ffffff', text = '保存', down = false, ...defaultProps } = this.props;
return (<View style={{ backgroundColor: bgColor }}>
<Button
type={!down ? 'ghost' : 'primary'}
{...defaultProps}
onClick={this.handler.bind(this)}
>
{text}
</Button>
</View>);
}
}
export class BottomButton extends Component {
componentWillMount() {
this.state = { showWaiting: false };
const { throttle = true } = this.props;
const handler = (e) => {
if (this.props.disabled) return;
this.setState({ showWaiting: true });
this.timer = setTimeout(() => this.setState({ showWaiting: false }), 3000);
this.props.onPress && this.props.onPress(e);
};
this.staticHandler = throttle ? _.throttle(handler, 6000, { trailing: false }) : handler;
}
componentWillUnmount() {
this.timer && clearTimeout(this.timer);
}
render() {
const {
bgColor = '#00abea', disabled = false, text = '保存',
waitingTitle = '正在处理数据', showIcon = true,
} = this.props;
return (<TouchableOpacity onPress={this.staticHandler}>
<View style={[styles.bottomButtonWrapper, { backgroundColor: bgColor }]}>
<Text style={{ color: disabled ? '#ccc' : '#fff', fontSize: 18 }}>{text}</Text>
{
showIcon && this.state.showWaiting &&
<ActivityIndicator color="#fff" size="large" style={styles.bottomButtonWaiting} />
}
<Modal
visible={false} transparent onRequestClose={() => {
}}
><Waiting title={waitingTitle} /></Modal>
</View>
</TouchableOpacity>);
}
}
export const Waiting = ({ title = '正在努力加载' }) => {
return (<View style={styles.waitingWrapper}>
<ActivityIndicator color="#fff" size="large" />
<Text style={styles.waitingText}>{`${title}
请稍候...`}</Text>
</View>);
};
export const Label = ({ title, whiteBg, onPress, showIcon }) => {
return (
<TouchableOpacity
onPress={onPress}
>
<View style={[styles.label, { backgroundColor: whiteBg ? '#fff' : '#f5f5f9' }]}>
<Text>{title}</Text>
{showIcon && <View style={styles.rightArrow}>
<Icon type={'\uE61F'} size={20} color="#ccc" />
</View>
}
</View>
</TouchableOpacity>);
};
export const Info = ({ data, twoCol }) => {
const items = [];
/* eslint-disable guard-for-in */
for (const p in data) {
const oneLine = p[0] === '_';// 非调试状态不支持startsWith,所以采用此方式
items.push(<View
key={Math.random()}
style={oneLine ? styles.infoItem : (twoCol ? styles.infoItem2 : styles.infoItem)}
><Text>
{p.substr(oneLine ? 1 : 0)}:{data[p]}
</Text></View>);
}
return (
<View style={styles.infoTable}>
{items}
</View>
);
};
export const Memo = ({ data }) => {
const { kind, memo, images, time } = data;
if (!memo && !images && images.length) return;
return (
<View style={{ backgroundColor: '#fff', marginTop: 10 }}>
<Flex style={styles.memoBar} justify="between">
<Text>{kind}</Text>
<Text style={styles.time}>{moment(time).format('YYYY-MM-DD HH:mm:ss')}</Text>
</Flex>
{memo && memo.length > 0 &&
<View style={styles.memoBody}>
<Text style={styles.memoText}>{memo}</Text>
</View>}
{images && images.length > 0 && <Images files={images} />}
</View>
);
};
export const Memos = ({ data }) => {
const items = data.map(d => <Memo key={Math.random()} data={d} />);
return (
<View style={{ backgroundColor: '#fff' }}>
{items}
</View>
);
};
export class Images extends Component {
state = {
visible: false,
index: 0,
};
render() {
const files = (this.props.files || []).map(f => ({ url: f }));
const { visible, index } = this.state;
const images = files.map((f, i) => (
<TouchableOpacity
key={i}
onPress={() => {
this.setState({ visible: true, index: i });
}}
>
<Image
style={styles.image}
source={{ uri: f.url }}
/>
</TouchableOpacity>
));
let rest = files.length % 4;
if (rest) {
rest = 4 - rest;
_.range(rest).map((r, j) => images.push(<View
key={files.length + j}
style={styles.image}
/>));
}
return (<View>
<View style={styles.imageContainer}>
{images}
</View>
<ImagePreview
visible={visible}
index={index}
images={files}
onPress={() => {
this.setState({ visible: false });
}}
/>
</View>);
}
}
export const SubOrder = ({ data, navigation }) => {
return (<TouchableOpacity
data={data} onPress={() => {
navigation.navigate('SubOrderInfo', data);
}}
>
<View style={styles.subOrder}>
<Image
style={{ width: 80, height: 80, borderRadius: 5 }}
source={{ uri: data.cover }}
/>
<View style={styles.subOrderInfo}>
<Text style={{ height: 20 }}>{data.name}</Text>
<Text>{data.washKind}</Text>
</View>
<Icon type="right" size="md" color="#ccc" />
</View>
</TouchableOpacity>);
};
export const SubOrderNav = ({ id, cover, productName, navigation }) => {
return (<TouchableOpacity
onPress={() => {
navigation.navigate('SubOrderDetail', { id, cover });
}}
>
<View style={[styles.subOrder, { borderBottomWidth: 0 }]}>
<Image
style={{ width: 80, height: 80, borderRadius: 5 }}
source={{ uri: cover }}
/>
<View style={styles.subOrderInfo}>
<Text style={{ height: 20 }}>{productName}</Text>
</View>
<Icon type="right" size="md" color="#ccc" />
</View>
</TouchableOpacity>);
};
export const SubOrders = ({ data, navigation }) => {
return (<View>
{data.map(d => <SubOrder key={Math.random()} data={d} {...{ navigation }} />)}
</View>);
};
export const TipBadge = ({ title = '开始处理', subTitle, icon = '\ue630' }) => {
return (<View style={styles.tipBadge}>
<Icon type={icon} color="#118ee9" size={48} />
<Text style={styles.tipBadgeTitle}>{title}</Text>
{subTitle && subTitle !== '' && <Text style={styles.tipBadgeSubTitle}>{subTitle}</Text>}
</View>);
};
/* eslint-disable no-mixed-operators */
const loadingWidth = 100;
const focusBoxSize = width / 1.5;
const styles = StyleSheet.create({
bottomButtonWrapper: {
height: 50,
marginTop: 6,
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
},
waitingWrapper: {
position: 'absolute',
top: highness / 2 - 50,
left: width / 2,
width: loadingWidth,
height: loadingWidth,
marginTop: -loadingWidth / 2,
marginLeft: -loadingWidth / 2,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
borderRadius: 10,
},
waitingText: {
marginTop: 10,
color: '#fff',
textAlign: 'center',
lineHeight: 20,
fontSize: 12,
},
waitingWrapper4BottomButton: {
width: loadingWidth,
height: loadingWidth,
marginTop: 100,
marginLeft: 100,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
borderRadius: 10,
},
label: {
borderLeftWidth: 2,
borderLeftColor: '#00abea',
paddingLeft: 5,
height: 40,
justifyContent: 'space-between',
flexDirection: 'row',
alignItems: 'center',
},
infoTable: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'flex-start',
alignItems: 'center',
paddingVertical: 5,
paddingHorizontal: 10,
backgroundColor: '#fff',
},
infoItem: {
width: '100%',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#edd',
},
infoItem2: {
width: '50%',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#edd',
},
memoBar: {
width: '90%',
},
memoBody: {
paddingTop: 12,
paddingBottom: 10,
},
memoText: {
fontSize: 12,
marginLeft: 8,
color: '#61b5ab',
},
time: {
fontSize: 12,
},
imageContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
alignItems: 'center',
margin: 5,
},
image: {
width: 75,
height: 75,
borderRadius: 5,
margin: 3,
},
subOrder: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#fff',
borderBottomColor: '#ccc',
borderBottomWidth: 1,
padding: 10,
paddingRight: 30,
},
subOrderInfo: {
flexDirection: 'column',
alignItems: 'flex-start',
flex: 1,
marginLeft: 10,
},
tipBadge: { flexDirection: 'column', alignItems: 'center', marginVertical: 30 },
tipBadgeTitle: { top: 20, fontSize: 16 },
tipBadgeSubTitle: { fontSize: 14, fontWeight: 'bold', marginTop: 30 },
bottomButtonWaiting: {
position: 'absolute',
right: 20,
top: '50%',
marginTop: -30,
height: 60,
width: 60,
},
preview: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: focusBoxSize,
height: focusBoxSize,
borderWidth: 1,
borderColor: '#0f0',
},
closeScaning: {
position: 'absolute',
bottom: 20,
width: 100,
height: 40,
backgroundColor: '#fff',
borderRadius: 10,
opacity: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
codeInputBarWrapper: {
backgroundColor: '#fff',
padding: 5,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
codeInputBarBox: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 10,
paddingHorizontal: 5,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
flex: 1,
marginRight: 5,
},
codeInputBar: {
flex: 1,
height: 25,
lineHeight: 18,
fontSize: 14,
padding: 0,
},
codeInputWrapper: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
});