Przeglądaj źródła

add app/pages/chargeV2/Charging.js

wudebin 6 miesięcy temu
rodzic
commit
273c5efdb5
1 zmienionych plików z 671 dodań i 0 usunięć
  1. 671 0
      Strides-SPAPP/app/pages/chargeV2/Charging.js

+ 671 - 0
Strides-SPAPP/app/pages/chargeV2/Charging.js

@@ -0,0 +1,671 @@
+/**
+ * 充电中的页面组件
+ * @邠心vbe on 2021/04/13
+ */
+import React, { useRef, useEffect, useState } from 'react';
+import { Animated, View, Easing, StyleSheet, Image, ImageBackground, TextInput } from 'react-native';
+import VbeRadialGradient from '../../components/VbeRadialGradient';
+import Modal from 'react-native-modal';
+import { ModalProps } from '../../components/BottomModal';
+import Button, { ElevationObject, ViewHeight } from '../../components/Button';
+import ChargeItemSelect from '../../icons/ChargeItemSelect';
+import { DialogMaxWidth } from './InfoDialog';
+import { QRResult } from '../charge/QRScan';
+import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg';
+import utils from '../../utils/utils';
+import TextView from '../../components/TextView';
+
+export const circleSize = $vw(50.66) < 300 ? $vw(50.66) : 300;
+
+const batterySize = 0.463 * circleSize;
+const batteryWidth = 0.659*batterySize;
+
+export const TypeImage = {
+  AC: require('../../images/charge/ic-type-ac.png'),
+  DC: require('../../images/charge/ic-type-dc.png'),
+  CHADEMO: require('../../images/charge/ic-type-chademo.png')
+}
+
+export const TypeImageList = [
+  {
+    name: 'AC',
+    key: 'AC',
+    icon: require('../../images/charge/ic-type-ac.png')
+  }, {
+    name: 'DC',
+    key: 'DC',
+    icon: require('../../images/charge/ic-type-dc.png')
+  }, /*{
+    name: 'Chademo',
+    key: 'CHADEMO',
+    icon: require('../../images/charge/ic-type-chademo.png')
+  }*/
+]
+
+export const getConnectTypeByKey = (key) => {
+  for (let item of TypeImageList) {
+    if (item.key == key) {
+      return item;
+    }
+  }
+}
+
+export const CircleAnimate = ({isStart = false}) => {
+  var rotate = useRef(new Animated.Value(0)).current;
+
+  const spins = () => {
+    Animated.timing(rotate, {
+      toValue: 1,
+      duration: 10000,
+      easing: Easing.linear,
+      useNativeDriver: true
+    }).start(() => {
+      rotate.setValue(0);
+      spins();
+    });
+  }
+
+  useEffect(() => {
+    if (isStart) {
+      spins();
+    }
+  }, [rotate]);
+
+  const spin = rotate.interpolate({
+    inputRange: [0, 1],
+    outputRange: ['0deg', '360deg']
+  })
+
+  return (
+    <Animated.View
+      style={[
+        styles.chargeCircle, {
+          transform: [{ 
+            rotate: spin 
+          }]
+        }
+      ]}>
+      { isStart &&
+        <View style={{ width: 25, height: 25, marginTop: -11}}>
+          <VbeRadialGradient
+            x="50%" y="50%" rx="50%" ry="50%"
+            colorList={[
+              {offset: '0%', color: '#FFF', opacity: .8},
+              {offset: '40%', color: '#FFF', opacity: .5},
+              {offset: '70%', color: '#FFF', opacity: .3},
+              {offset: '100%', color: '#FFF', opacity: 0}
+            ]}/>
+        </View>
+      }
+    </Animated.View>
+  );
+}
+
+export const BatteryView = ({soc, isPending, isCharging}) => {
+  var [powerPercent, setPercent] = useState(-1);
+  /*var [powerText, setText] = useState(0);
+
+  const autoCharge = () => {
+    setTimeout(() => {
+      const p = powerPercent + 0.001
+      setPercent(Number(p.toFixed(4)))
+      setText((powerPercent * 100).toFixed(0))
+      if (powerPercent >= 1) {
+        onComplete();
+      }
+    }, 50 + powerPercent * 100);
+  }
+
+  useEffect(() => {
+    if (run && powerPercent <= 1) {
+      autoCharge();
+    }
+  }, [powerPercent])*/
+  useEffect(() => {
+    if (isCharging) {
+      try {
+        var s = '-1' + soc;
+        var d = '' + parseInt(s);
+        d = d.replace('-1', '');
+        if (d) {
+          setPercent(parseInt(d))
+        } else {
+          setPercent(-1);
+        }
+      } catch (e) {
+        setPercent(-1);
+      }
+    }
+  }, [soc]);
+
+  const getOpacity = (unit) => {
+    var op = 1 * (powerPercent + unit);
+    return op < 1 ? op : 1;
+  }
+
+  const getHeight = () => {
+    if (powerPercent > 1) {
+      return batterySize * powerPercent / 100;
+    } else if (powerPercent > 0) {
+      return batterySize * powerPercent;
+    } else {
+      return 0
+    }
+  }
+
+  return (
+    isCharging
+    ? <View style={ui.center}>
+        <ImageBackground
+          style={{
+            width: circleSize,
+            height: circleSize,
+            margin: 32,
+            padding: 32,
+            alignItems: 'center',
+            justifyContent: 'center'
+          }}
+          source={require('../../images/charge/ic-charge-circle.png')}>
+          <View style={styles.chargingView}>
+            <View style={styles.plusLeftView}>
+              <Image
+                style={[styles.plusMiddle, { opacity: getOpacity(0.5)}]}
+                source={require('../../images/charge/ic-plus-middle.png')}/>
+              <Image
+                style={[styles.plusSmall, { opacity: getOpacity(0.4)}]}
+                source={require('../../images/charge/ic-plus-small.png')}/>
+            </View>
+            <View style={styles.batteryView}>
+              <View style={[styles.batteryIcon]}>
+                <Image
+                  style={styles.batteryIcon}
+                  source={require('../../images/charge/ic-battery-0.png')}/>
+                <View style={[styles.batteryLayer, { height: getHeight() }]}>
+                  <Image
+                    style={[styles.batteryIcon]}
+                    source={require('../../images/charge/ic-battery-1.png')}/>
+                </View>
+              </View>
+              { isPending
+                ? <TextView style={styles.batterySoc}>{$t('charging.statusInitiating')}</TextView>
+                : powerPercent != -1
+                  ? <TextView style={styles.batteryPercent}>{powerPercent}%</TextView>
+                  : <TextView style={styles.batterySoc}>{$t('charging.statusInCharging')}</TextView>
+              }
+            </View>
+            <View style={styles.plusRightView}>
+              <Image
+                style={[styles.plusLarge, { opacity: getOpacity(0.6)}]}
+                source={require('../../images/charge/ic-plus-large.png')}/>
+            </View>
+          </View>
+          <CircleAnimate isStart={true}/>
+        </ImageBackground>
+      </View>
+    : <View style={styles.completeView}>
+        <Image
+          style={styles.disconnectIcon}
+          source={require('../../images/charge/charge-complete.png')}/>
+        <TextView style={styles.completeTip}>{$t('charging.tipsDisconnectConnector')}</TextView>
+      </View>
+  );
+}
+
+export const DashboardView = ({isCharging=false, connectorInfo={}}) => {
+  if (isCharging) {
+    return (
+      <View style={styles.dashboardStartView}>
+        <View style={styles.dashboardGradientView}>
+          <Svg width="365" height="132" viewBox="0 0 365 132">
+            <Rect width="365" height="132" fill="url(#paint0_linear_4415_6112)"/>
+            <Defs>
+              <LinearGradient id="paint0_linear_4415_6112" x1="169.5" y1="136.5" x2="170.5" y2="5.49999" gradientUnits="userSpaceOnUse">
+                <Stop stopColor="#5BFA00"/>
+                <Stop offset="1" stopColor="#009E81"/>
+              </LinearGradient>
+            </Defs>
+          </Svg>
+        </View>
+        <TextView style={styles.dashboardTitleWhite}>{$t('charging.chargingInProgress')}</TextView>
+        <View style={styles.dashboardItemGroup}>
+          <View style={styles.dashboardItemView}>
+            <TextView style={styles.dashboardItemValue}>{utils.minutes2HHmm(connectorInfo?.timeElapsed ?? 0)}</TextView>
+            <TextView
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.dashboardItemTitle}>{$t('charging.labelTimeElapsed')}</TextView>
+          </View>
+          <View style={styles.dashboardItemView}>
+            <TextView style={styles.dashboardItemValue}>{utils.toFixed(connectorInfo?.totalKWhDelivered, 2) ?? "0"} kWh</TextView>
+            <TextView
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalkWh')}</TextView>
+          </View>
+          <View style={styles.dashboardItemView}>
+            {/* <Text style={styles.dashboardItemValueWeight}>{currency} {utils.toFixed(connectorInfo?.totalCharges, 2) ?? "0.0"}</Text> */}
+            <TextView style={styles.dashboardItemValueWeight}>{connectorInfo?.totalCharges ?? "$0.0"}</TextView>
+            <TextView
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalCharges')}</TextView>
+          </View>
+        </View>
+      </View>
+    )
+  } else {
+    return (
+      <View style={styles.dashboardGreyView}>
+        <TextView style={styles.dashboardTitle}>{$t('charging.pressStartToBegin')}</TextView>
+        <View style={styles.dashboardItemGroup}>
+          <View style={styles.dashboardItemView}>
+            <TextView style={styles.dashboardItemValue}>-</TextView>
+            <TextView
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.dashboardItemTitle}>{$t('charging.labelTimeElapsed')}</TextView>
+          </View>
+          <View style={styles.dashboardItemView}>
+            <TextView style={styles.dashboardItemValue}>-</TextView>
+            <TextView
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalkWh')}</TextView>
+          </View>
+          <View style={styles.dashboardItemView}>
+            <TextView style={styles.dashboardItemValueWeight}>-</TextView>
+            <TextView
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.dashboardItemTitle}>{$t('charging.labelTotalCharges')}</TextView>
+          </View>
+        </View>
+      </View>
+    )
+  }
+}
+
+export const EnterStationDialog = ({visible, stationId, onConfirm, onClose}) => {
+  var [inputStationId, setInput] = useState('')
+  var [iosKillDialog, killDialog] = useState(true)
+
+  const enterStatioinId= () => {
+    //console.log(inputStationId);
+    if (inputStationId) {
+      if (isIOS && iosKillDialog) {
+        onClose();
+        killDialog(false);
+      } else {
+        QRResult.applyInputStation(inputStationId, stationId, (success, err) => {
+          setInput('')
+          if (success) {
+            if (onConfirm) onConfirm()
+          } else if (err) {
+            toastShort(err)
+          }
+          if (onClose) onClose()
+        });
+      }
+    } else {
+      toastShort($t('charging.plsInputStationId'));
+    }
+  }
+
+  useEffect(() => {
+    if (!visible && !iosKillDialog) {
+      killDialog(true);
+      setTimeout(() => {
+        enterStatioinId();
+      }, 100);
+    }
+  }, [iosKillDialog])
+
+  return (
+    iosKillDialog
+    ? <Modal
+        isVisible={visible}
+        {...ModalProps}
+        onBackdropPress={() => onClose}
+        onBackButtonPress={() => onClose}>
+        <View style={styles.stationDialog}>
+          <TextView style={styles.stationInputTitle}>{$t('charging.enterStationId')}</TextView>
+          <TextInput
+            style={styles.stationInput}
+            allowFontScaling={false}
+            defaultValue={inputStationId}
+            placeholder={$t('charging.hintExampleStationId')}
+            placeholderTextColor={textPlacehoder}
+            onChangeText={text => setInput(text)}
+          />
+          <View style={styles.dialogButtons}>
+            <Button
+              textSize={17}
+              style={styles.buttonCancel}
+              viewStyle={ViewHeight(42)}
+              text={$t('common.close')}
+              textColor={textCancel}
+              onClick={onClose}/>
+            <Button
+              textSize={17}
+              style={styles.buttonOK}
+              viewStyle={ViewHeight(42)}
+              text={$t('common.confirm')}
+              onClick={() => enterStatioinId()}/>
+          </View>
+        </View>
+      </Modal>
+    : <></>
+  )
+}
+
+const styles = StyleSheet.create({
+  chargingView: {
+    width: circleSize,
+    height: circleSize,
+    flexDirection: 'row',
+    alignItems: 'center'
+  },
+  chargeCircle: {
+    top: 0,
+    left: 0,
+    zIndex: 10,
+    width: circleSize,
+    height: circleSize,
+    position: 'absolute',
+    alignItems: 'center',
+    borderRadius: circleSize
+  },
+  batteryView: {
+    paddingTop: 8,
+    paddingLeft: 12,
+    paddingRight: 12,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  batteryIcon: {
+    width: batteryWidth,
+    height: batterySize,
+    position: 'relative',
+    justifyContent: 'flex-end',
+  },
+  batteryLayer: {
+    left: 0,
+    right: 0,
+    height: 0,
+    width: batteryWidth,
+    overflow: 'hidden',
+    position: 'absolute',
+    justifyContent: 'flex-end',
+  },
+  batteryPercent: {
+    color: textPrimary,
+    fontSize: 22,
+    textAlign: 'center',
+    paddingTop: 10,
+    paddingBottom: 8
+  },
+  batterySoc: {
+    color: textPrimary,
+    fontSize: 18,
+    textAlign: 'center',
+    paddingTop: 10,
+    paddingBottom: 10
+  },
+  plusLeftView: {
+    flex: 1,
+    height: batterySize + 10,
+    marginBottom: 12,
+    alignItems: 'flex-end',
+    justifyContent: 'space-around'
+  },
+  plusRightView: {
+    flex: 1,
+    justifyContent: 'center'
+  },
+  plusLarge: {
+    width: 24,
+    height: 24
+  },
+  plusMiddle: {
+    width: 18,
+    height: 18
+  },
+  plusSmall: {
+    width: 12,
+    height: 12,
+    marginBottom: 8
+  },
+  selectView: {
+    position: 'relative'
+  },
+  selectIcon: {
+    width: 18,
+    height: 18
+  },
+  selectIconAbs: {
+    top: -2,
+    right: -4,
+    zIndex: 2,
+    position: 'absolute'
+  },
+  completeView: {
+    height: circleSize,
+    marginBottom: 16,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  disconnectIcon: {
+    width: 100,
+    height: 100
+  },
+  completeTip: {
+    width: circleSize,
+    color: textPrimary,
+    fontSize: 16,
+    paddingLeft: 16,
+    paddingRight: 16,
+    textAlign: 'center'
+  },
+  stationDialog: {
+    padding: 16,
+    marginLeft: 'auto',
+    marginRight: 'auto',
+    borderRadius: isIOS ? 20 : 3,
+    width: DialogMaxWidth,
+    backgroundColor: colorLight
+  },
+  stationInput: {
+    ...$padding(4, 10),
+    minHeight: 40,
+    borderRadius: 3,
+    backgroundColor: '#F0F0F0'
+  },
+  stationInputTitle: {
+    fontSize: 15,
+    textAlign: 'center',
+    paddingBottom: 16
+  },
+  dialogButtons: {
+    paddingTop: 24,
+    paddingBottom: 8,
+    flexDirection: 'row'
+  },
+  buttonOK: {
+    flex: 1,
+    marginLeft: 12,
+  },
+  buttonCancel: {
+    flex: 1,
+    borderWidth: 1,
+    borderColor: colorCancel,
+    backgroundColor: pageBackground
+  },
+  dashboardGreyView: {
+    padding: 16,
+    marginTop: 16,
+    marginBottom: 16,
+    borderRadius: 6,
+    ...ElevationObject(5),
+    backgroundColor: pageBackground
+  },
+  dashboardStartView: {
+    padding: 16,
+    marginTop: 16,
+    marginBottom: 16,
+    borderRadius: 6,
+    ...ElevationObject(5)
+  },
+  dashboardGradientView: {
+    top: 0,
+    left: 0,
+    right: 0,
+    bottom: 0,
+    borderRadius: 6,
+    overflow: 'hidden',
+    position: "absolute",
+    backgroundColor: "#3CDB2B"
+  },
+  dashboardTitle: {
+    color: textPrimary,
+    fontSize: 25,
+    fontWeight: 'bold',
+    textAlign: 'center',
+    paddingBottom: 16
+  },
+  dashboardTitleWhite: {
+    color: textLight,
+    fontSize: 25,
+    fontWeight: 'bold',
+    textAlign: 'center',
+    paddingBottom: 16
+  },
+  dashboardItemGroup: {
+    marginRight: -12,
+    marginBottom: -32,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  dashboardItemView: {
+    flex: 1,
+    marginRight: 12,
+    borderRadius: 6,
+    ...$padding(10, 6, 14),
+    ...ElevationObject(5),
+    backgroundColor: colorLight
+  },
+  dashboardItemTitle: {
+    color: textCancel,
+    fontSize: 12,
+    textAlign: 'center',
+    paddingTop: 2
+  },
+  dashboardItemValue: {
+    color: textPrimary,
+    fontSize: 14,
+    textAlign: 'center'
+  },
+  dashboardItemValueWeight: {
+    color: textPrimary,
+    fontSize: 14,
+    fontWeight: 'bold',
+    textAlign: 'center'
+  }
+});
+
+export const SelectableIcon = ({selected, children}) => {
+  return (
+    <View style={styles.selectView}>
+      {selected &&
+        <View style={[styles.selectIcon, children && styles.selectIconAbs]}>
+          <ChargeItemSelect size={18} selected={true}/>
+        </View>
+      }
+      {children}
+    </View>
+  );
+}
+
+export const ChargeStyle = StyleSheet.create({
+  stationInfoView: {
+    padding: 12,
+    borderRadius: 10,
+    marginBottom: 12,
+    alignItems: 'center',
+    flexDirection: 'row',
+    ...ElevationObject(5),
+    backgroundColor: colorLight,
+    justifyContent: 'space-between'
+  },
+  infoGroup: {
+    ...$padding(4, 2),
+    alignItems: 'center'
+  },
+  infoTitle: {
+    color: '#999',
+    fontSize: 12,
+    paddingTop: 1
+  },
+  infoText: {
+    color: textPrimary,
+    fontSize: 13,
+    fontWeight: 'bold'
+  },
+  infoBoldNumber: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingTop: 3,
+    fontWeight: 'bold'
+  },
+  infoStatus: {
+    fontSize: 14,
+    fontWeight: 'bold'
+  },
+  infoIcon: {
+    width: 38,
+    height: 38
+  },
+  itemDivide: {
+    borderTopWidth: 1,
+    borderTopColor: '#eee'
+  },
+  statusSelected: {
+    color: textPrimary,
+    ...$padding(4, 11),
+    backgroundColor: colorAccent
+  },
+  statusAvailable: {
+    color: '#90DB0A'
+  },
+  statusUnavailable: {
+    color: '#666',
+    fontSize: 12,
+    paddingBottom: 1
+  },
+  statusAuthenticated: {
+    color: '#90DB0A',
+    fontSize: 12,
+    paddingBottom: 1
+  },
+  statusError: {
+    color: '#EF3340',
+    fontSize: 12,
+    paddingBottom: 1
+  },
+  statusWarning: {
+    color: textLight,
+    backgroundColor: colorAccent
+  },
+  rateText: {
+    color: textPrimary,
+    fontSize: 14,
+  },
+  ratePrice: {
+    color: '#000',
+    fontSize: 14,
+  },
+  authText: {
+    color: '#000',
+    fontSize: 12,
+    paddingTop: 2
+  }
+});