UNPKG

elephant-com

Version:

the general component for elephant washing shoes

580 lines (549 loc) 15.4 kB
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', }, });