فهرست منبع

add app/pages/charge/Charge.js

wudebin 6 ماه پیش
والد
کامیت
1befdf9c9c
1فایلهای تغییر یافته به همراه742 افزوده شده و 0 حذف شده
  1. 742 0
      Strides-SPAPP/app/pages/charge/Charge.js

+ 742 - 0
Strides-SPAPP/app/pages/charge/Charge.js

@@ -0,0 +1,742 @@
+/**
+ *  充电页面
+ * @邠心vbe on 2021/04/12
+ */
+import React, { Component } from 'react';
+import { View, Text, StyleSheet, ImageBackground, Image, TextInput } from 'react-native';
+import apiCharge from '../../api/apiCharge';
+import Button from '../../components/Button';
+import Dialog from '../../components/Dialog';
+import { PageList } from '../Router';
+import { BatteryView, ChargeStyle, circleSize, EnterStationDialog, TypeImage } from './Charging';
+import Payment from '../wallet/Payment';
+import { QRResult } from './QRScan';
+import { ErrorDialog } from './InfoDialog';
+import utils from '../../utils/utils';
+import TextView from '../../components/TextView';
+import { PaymentDefault, PAYTYPE } from '../payment/PaymentConfig';
+
+export default class Charge extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      available: false,
+      isPrivate: false,
+      refreshId: 0,
+      isStart: false,
+      isPending: false,
+      isCharging: false,
+      isAuthentic: false,
+      rateList: [],
+      selectRate: '',
+      connectorInfo: {},
+      stationInfo: {},
+      lastUpdated: '',
+      errorCode: 'A9',
+      errorMessage: '',
+      showErrorDialog: false,
+      showStationDialog: false,
+      curerntPerUser: undefined,
+      //currentPayment: PAYTYPE.CREDIT_WALLET,
+      //currentPaytype: "Credit Wallet",
+      currentPayment: PaymentDefault.DEFAULT.payType,
+      currentPaytype: PaymentDefault.DEFAULT.payName
+    };
+    this.changeMethod = false;
+    this.canAutoRefresh = false;
+    this.inputStationId = '';
+  }
+
+  componentDidMount() {
+    this.canAutoRefresh = true;
+    if (this.props.stationInfo) {
+      this.setState({
+        rateList: this.props.stationInfo.rateList ?? [],
+        stationInfo: this.props.stationInfo
+      }, () => {
+        this.refreshAvailable();
+        this.checkIsCharge();
+      });
+    }
+  }
+
+  componentDidUpdate() {
+    if (this.props.visible && this.props.refreshId != this.state.refreshId) {
+      this.onMethodChanged();
+      this.setState({
+        refreshId: this.props.refreshId,
+        rateList: this.props.stationInfo.rateList ?? [],
+        stationInfo: this.props.stationInfo
+      }, () => this.refreshAvailable());
+      if (QRResult.haveResult()) {
+        const info = QRResult.getResult()
+        console.log('QRResult', info);
+        this.setState({
+          isAuthentic: true,
+          connectorInfo: info
+          //soc: info.chargeType == 'AC' ? 0 : 'In Charging'
+        });
+        this.checkChargeStatus();
+      } else if (this.state.isStart) {
+        this.checkIsCharge();
+      } else {
+        this.checkChargeStatus();
+      }
+    }
+  }
+
+  componentWillUnmount() {
+    this.canAutoRefresh = false;
+  }
+
+  //刷新可用充电接口
+  refreshAvailable() {
+    const info = this.state.stationInfo
+    const all = info?.allConnector;
+    if (info.siteType == 'Private') {
+      this.setState({
+        isPrivate: true
+      })
+    }
+    if (all) {
+      this.setState({
+        available: !all.available > 0
+      });
+    }
+  }
+
+  enterStatioinId() {
+    if (QRResult.haveResult()) {
+      const info = QRResult.getResult()
+      console.log('EnterResult', info);
+      this.setState({
+        isAuthentic: true,
+        connectorInfo: info
+        //soc: info.chargeType == 'AC' ? 0 : 'In Charging'
+      });
+      this.checkChargeStatus();
+    }
+  }
+
+  onMethodChange() {
+    this.changeMethod = true;
+    startPage(PageList.paymentMethod, {info: this.state.connectorInfo, type: this.state.currentPayment});
+  }
+
+  onMethodChanged() {
+    if (this.changeMethod) {
+      this.changeMethod = false;
+      if (global.paymentOption?.title) {
+        this.setState({
+          curerntPerUser: global.paymentOption.amount,
+          currentPayment: global.paymentOption.value,
+          currentPaytype: global.paymentOption.title
+        }, () => {
+          global.paymentOption= {}
+        })
+      }
+    }
+  }
+
+  //扫码之前-站点信息Section
+  StationInfo() {
+    return (
+      <View style={styles.listView}>
+        <View style={ChargeStyle.stationInfoView}>
+          <ImageBackground
+            style={{
+              width: 42,
+              height: 42
+            }}
+            source={require('../../images/charge/icon-station-no.png')}>
+            <TextView style={{
+              left: 0,
+              right: 0,
+              bottom: 1,
+              fontSize: 8,
+              textAlign: 'center',
+              position: 'absolute'
+            }}>{this.state.connectorInfo.connectorId}</TextView>
+          </ImageBackground>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Type</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Power</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW{/*this.state.connectorInfo.rateType*/}</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Rate</TextView>
+            <TextView style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</TextView>
+          </View>
+          { this.state.connectorInfo.isCheckThrough
+          ? <View style={ChargeStyle.infoGroup}>
+              <MaterialIcons name='check-circle' size={18} color='#90DB0A'/>
+              <TextView style={ChargeStyle.authText}>Authenticated</TextView>
+            </View>
+          : <View style={ChargeStyle.infoGroup}>
+              <MaterialCommunityIcons name='close-circle' size={18} color='#FF6666'/>
+              <TextView style={ChargeStyle.authText}>Not Connected</TextView>
+            </View>
+          }
+        </View>
+      </View>
+    );
+  }
+  //扫码之前-站点信息Section-end
+
+  //扫码之后-选择的站点信息Section
+  UseStationInfo() {
+    return (
+      <View style={[styles.listView, { marginTop: 16}]}>
+        <View style={ChargeStyle.stationInfoView}>
+          <ImageBackground
+            style={{
+              width: 42,
+              height: 42
+            }}
+            source={require('../../images/charge/icon-station-no.png')}>
+            <TextView style={{
+              left: 0,
+              right: 0,
+              bottom: 1,
+              fontSize: 8,
+              color: textPrimary,
+              textAlign: 'center',
+              position: 'absolute'
+            }}>{this.state.connectorInfo.connectorId}</TextView>
+          </ImageBackground>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Type</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Power</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Rate</TextView>
+            <TextView style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={styles.inUse}>In Use</TextView>
+          </View>
+        </View>
+        <View style={[ChargeStyle.stationInfoView, ChargeStyle.itemDivide]}>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Time Elapsed</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.timeElapsed?.toFixed(0) ?? 0} Minutes</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Total kWh Delivered</TextView>
+            <TextView style={ChargeStyle.infoText}>{this.state.connectorInfo.totalKWhDelivered ?? 0} kWh</TextView>
+          </View>
+          <View style={ChargeStyle.infoGroup}>
+            <TextView style={ChargeStyle.infoTitle}>Total Charges</TextView>
+            <TextView style={ChargeStyle.infoText}>{currency} {this.state.connectorInfo.totalCharges?.toFixed(2) ?? '0.0'}</TextView>
+          </View>
+        </View>
+      </View>
+    );
+  }
+  //扫码之后-选择的站点信息Section-end
+
+  //初始页面-扫码认证之前
+  StepRateView() {
+    return (
+      <>
+        <TextView style={styles.title}>Rates</TextView>
+        <View style={styles.listView}>
+          { this.state.rateList.length > 0
+            ? this.state.rateList.map((item, index) => {
+                return (
+                  <View key={index} style={[ChargeStyle.stationInfoView, index > 0 ? ChargeStyle.itemDivide : {}]}>
+                    <Image
+                      style={ChargeStyle.infoIcon}
+                      source={item.type?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
+                    <View style={ChargeStyle.infoGroup}>
+                      <TextView style={ChargeStyle.infoTitle}>Type</TextView>
+                      <TextView style={ChargeStyle.infoText}>{item.type}</TextView>
+                    </View>
+                    <View style={ChargeStyle.infoGroup}>
+                      <TextView style={ChargeStyle.infoTitle}>Power</TextView>
+                      <TextView style={ChargeStyle.infoText}>{item.power}</TextView>
+                    </View>
+                    <View style={ChargeStyle.infoGroup}>
+                      <TextView style={ChargeStyle.infoTitle}>Rate</TextView>
+                      <TextView style={ChargeStyle.infoText}>{item.rates}</TextView>
+                    </View>
+                    { item?.connectorCount?.available > 0
+                      ? <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</TextView>
+                      : <TextView style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</TextView>
+                    }
+                  </View>
+                );
+              })
+            : <Text style={ui.noData}>No Rates</Text>
+          }
+        </View>
+        <View style={styles.privateView}> 
+          { this.state.isPrivate &&
+            <Text style={styles.privateText}>NOTE: The charging stations are for private usage.</Text>
+          }
+        </View>
+        <Payment refreshId={this.state.refreshId}/>
+        <View style={styles.buttonGroup}>
+          <Button
+            style={styles.buttonLeft}
+            text='Scan QR'
+            //disabled={this.state.available}
+            onClick={() => {
+              startPage(PageList.scanqr, {actionDetail: false, id: this.state.stationInfo.id});
+            }}/>
+          <Button
+            style={styles.buttonRight}
+            text='Enter Station ID'
+            //disabled={this.state.available}
+            onClick={() => {
+              this.setState({
+                showStationDialog: true
+              })
+            }}/>
+        </View>
+      </>
+    );
+  }
+  //初始页面-扫码认证之前-end
+
+  //扫码认证之后-充电开始之前
+  StepStartView() {
+    return (
+      <>
+        <TextView style={styles.title}>Your Selection</TextView>
+        {this.StationInfo()}
+        <View style={ui.center}>
+          <ImageBackground
+            style={styles.batteryBorder}
+            source={require('../../images/charge/ic-charge-circle.png')}>
+            <Text style={{
+              color: textPrimary,
+              fontSize: 16,
+              lineHeight: 22,
+              textAlign: 'center'
+            }}>
+              Press<Text style={{padding: 10, fontWeight: 'bold'}}> Start </Text>to begin Charging
+            </Text>
+          </ImageBackground>
+        </View>
+        <Payment 
+          refreshId={this.state.refreshId}
+          payType={this.state.currentPaytype}
+          balance={this.state.curerntPerUser}
+          isPayPerUse={this.state.currentPayment == PAYTYPE.PAY_PER_USE}
+          onMethodChange={() => this.onMethodChange()}
+        />
+        
+        <Button
+          style={styles.buttonView}
+          text='Start'
+          elevation={1.5}
+          onClick={() => this.startCharge()}/>
+      </>
+    );
+  }
+  //扫码认证之后-充电开始之前-end
+
+  //正在充电页面
+  StepChargeView() {
+    return (
+      <>
+        {this.UseStationInfo()}
+
+        <BatteryView
+          soc={this.state.connectorInfo.batteryPercent}
+          isCharging={this.state.isCharging}
+          isPending={this.state.isPending}
+        />
+
+        { this.state.lastUpdated
+          ? <Text style={styles.updateTip}>{'Last updated at ' + this.state.lastUpdated + '\nPull down to refresh'}</Text>
+          : null
+        }
+
+        <Payment 
+          refreshId={this.state.refreshId}
+          payType={this.state.currentPaytype}
+          balance={this.state.curerntPerUser}
+        />
+        
+        <Button
+          style={styles.buttonView}
+          disabled={this.state.isPending}
+          text={this.state.isCharging ? 'Stop Charging' : 'Complete'}
+          elevation={1.5}
+          onClick={() => {
+            if (this.state.isCharging) {
+              Dialog.showDialog({
+                title: 'Stop Charging',
+                message: 'Are you sure stop charging?',
+                callback: ok => {
+                  if (ok == Dialog.BUTTON_OK) {
+                    this.stopCharge();
+                  }
+                }
+              });
+            } else {
+              this.stopCharge();
+            }
+          }}/>
+      </>
+    );
+  }
+  //正在充电页面-end
+
+  //自动刷新
+  autoCheckIsCharge() {
+    if (this.canAutoRefresh) {
+      this.checkIsCharge();
+    }
+  }
+
+  //自动刷新状态
+  autoCheckChargeStatus() {
+    setTimeout(() => {
+      if (this.canAutoRefresh) {
+        this.checkChargeStatus();
+      }
+    }, 10000);
+  }
+
+  //获取充电数据(百分比)
+  checkIsCharge(showError) {
+    const params = {
+      sitePk: this.props.stationInfo.id
+    }
+    apiCharge.checkIsCharging(params).then(res => {
+      this.setState({
+        isStart: true,
+        isCharging: true,
+        isAuthentic: true,
+        connectorInfo: res.data,
+        lastUpdated: utils.getNowHHmm()
+      });
+      if (this.canAutoRefresh) {
+        setTimeout(() => {
+          this.autoCheckIsCharge();
+        }, 30000);
+      }
+      if (!this.props.visible && this.props.onCharge)
+        this.props.onCharge()
+    }).catch((err) => {
+      //TODO 模拟测试
+      this.setState({
+        isStart: false,
+        isCharging: false
+      });
+      setTimeout(() => {
+        this.autoCheckIsCharge();
+      }, 30000);
+      if (showError) {
+        this.setState({
+          errorCode: 'A4',
+          showErrorDialog: true,
+          errorMessage: 'Your vehicle doesn’t seem to be charging. Please check your vehicle.'
+        });
+      }
+    });
+  }
+
+  //获取充电桩对应接口的状态
+  checkChargeStatus() {
+    //TODO 模拟测试
+    /*this.setState({
+      isStart: true,
+      isCharging: true,
+      isAuthentic: true
+    });
+    return;*/
+    const params = {
+      connectorId: this.state.connectorInfo.connectorId,
+      chargeBoxId: this.state.connectorInfo.chargeBoxId,
+    }
+    if (!params.chargeBoxId || !params.connectorId) {
+      return;
+    }
+    apiCharge.getCurrentStatus(params).then(res => {
+      if (res.data.status) {
+        switch (res.data.status) {
+          case 'Available': //可用的
+            this.state.connectorInfo.isCheckThrough = false;
+            this.setState({
+              isStart: false,
+              isPending: false,
+              isCharging: false,
+              connectorInfo: this.state.connectorInfo
+            });
+            break;
+          case 'Preparing': //已插入
+            this.state.connectorInfo.isCheckThrough = true;
+            this.setState({
+              isStart: false,
+              isPending: false,
+              isCharging: false,
+              available: true,
+              connectorInfo: this.state.connectorInfo
+            });
+            //this.checkIsCharge();
+            break;
+          case 'Charging': //正在充电
+            this.canAutoRefresh = true;
+            this.state.connectorInfo.isCheckThrough = true;
+            this.setState({
+              isPending: false,
+              connectorInfo: this.state.connectorInfo
+            });
+            this.checkIsCharge();
+            break;
+          case 'Initiating': //充电确认中
+            this.canAutoRefresh = true;
+            this.state.connectorInfo.isCheckThrough = true;
+            this.setState({
+              isStart: true,
+              isPending: true,
+              isCharging: true,
+              isAuthentic: true,
+              connectorInfo: this.state.connectorInfo
+            });
+            this.autoCheckChargeStatus();
+            break;
+          case 'SuspendedEVSE':
+            this.setState({
+              errorCode: 'A5',
+              showErrorDialog: true,
+              errorMessage: 'The charging station is unable to charge your vehicle.Please reauthenticate.'
+            });
+            break;
+          case 'SuspendedEV': //已连接上但未充电
+            this.checkIsCharge(true);
+            break;
+          case 'Reserved': //预定中
+            this.setState({
+              errorCode: 'A5',
+              showErrorDialog: true,
+              errorMessage: 'The charging station is reserved and unable to charge your vehicle.'
+            });
+            break;
+          case 'Finishing': //已完成
+            this.setState({
+              isStart: true,
+              isPending: false,
+              isCharging: false
+            });
+            break;
+          default:
+            this.setState({
+              isStart: false,
+              isPending: false,
+              isCharging: false
+            });
+            this.setState({
+              errorCode: 'A4',
+              showErrorDialog: true,
+              errorMessage: 'Your vehicle doesn’t seem to be charging. Please check your vehicle. (E0)'
+            });
+            break;
+        }
+      }
+    }).catch((err) => {
+      toastShort(err)
+      this.setState({
+        errorCode: 'A9',
+        showErrorDialog: true,
+        errorMessage: 'There seems to be an authentication error! Please try again'
+      });
+    })
+  }
+
+  //开始充电api
+  startCharge() {
+    if (this.state.connectorInfo.isCheckThrough) {
+      Dialog.showProgressDialog();
+      const params = {
+        chargeBoxId: this.state.connectorInfo.chargeBoxId,
+        connectorId: this.state.connectorInfo.connectorId
+      }
+      apiCharge.startCharge(params).then(res => {
+        this.setState({
+          isStart: true,
+          isPending: true,
+          isCharging: true
+        });
+        this.canAutoRefresh = true;
+        this.autoCheckChargeStatus();
+        /*setTimeout(() => {
+          this.autoCheckIsCharge();
+        }, 30000);*/
+        Dialog.dismissLoading();
+      }).catch((err) => {
+        //toastShort(err);
+        Dialog.dismissLoading();
+        this.setState({
+          errorCode: 'A4',
+          showErrorDialog: true,
+          errorMessage: ''+err
+        });
+      });
+    } else {
+      this.setState({
+        errorCode: 'A1',
+        showErrorDialog: true,
+        errorMessage: 'Your vehicle is not connected to the charging station. Please check the connector.'
+      });
+    }
+  }
+
+  //停止充电api
+  stopCharge() {
+    this.canAutoRefresh = false;
+    Dialog.showProgressDialog();
+    apiCharge.stopCharge().then(res => {
+      if (res.data.chargingPk) {
+        setTimeout(() => {
+          Dialog.dismissLoading();
+          this.setState({
+            isStart: false,
+            isPending: false,
+            isCharging: false
+          });
+          startPage(PageList.summary, { 
+            chargingPk: res.data.chargingPk,
+            id: this.state.stationInfo.id,
+            name: this.state.stationInfo.name,
+            address: this.state.stationInfo.address
+          });
+        }, 3000);
+      } else {
+        Dialog.dismissLoading();
+        toastShort('An error detected, please retry.')
+      }
+    }).catch((err) => {
+      Dialog.dismissLoading();
+      toastShort(err);
+      this.setState({
+        isStart: false,
+        isPending: false,
+        isCharging: false
+      });
+      //模拟进入结算页
+      /*startPage(PageList.summary, {
+        chargingPk: 1,
+        id: this.state.stationInfo.id,
+        name: this.state.stationInfo.name,
+        address: this.state.stationInfo.address
+      });*/
+    });
+  }
+
+  closeError() {
+    this.setState({
+      showErrorDialog: false,
+      showStationDialog: false
+    });
+  }
+
+  render() {
+    return (
+      <View style={{
+          paddingLeft: 16,
+          paddingRight: 16,
+          ...(this.props.style || {})
+        }}>
+        { this.state.isAuthentic //是否扫码认证
+          ? this.state.isStart   //是否开始充电
+            ? this.StepChargeView()
+            : this.StepStartView()
+          : this.StepRateView()
+        }
+        <ErrorDialog
+          visible={this.state.showErrorDialog}
+          code={this.state.errorCode}
+          message={this.state.errorMessage}
+          onClose={() => {
+            this.closeError();
+          }}
+        />
+        <EnterStationDialog
+          visible={this.state.showStationDialog}
+          stationId={this.state.stationInfo.id}
+          onConfirm={() => this.enterStatioinId()}
+          onClose={() => this.closeError()}
+        />
+      </View>
+    );
+  }
+}
+
+const styles = StyleSheet.create({
+  title: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 16
+  },
+  listView: {
+    padding: 8,
+    borderRadius: 8,
+    backgroundColor: '#F5F5F5'
+  },
+  batteryBorder: {
+    margin: 30,
+    padding: 32,
+    width: circleSize,
+    height: circleSize,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  buttonView: {
+    marginTop: 16,
+    marginBottom: 32
+  },
+  buttonGroup: {
+    marginTop: 16,
+    marginBottom: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  buttonLeft: {
+    flex: 1,
+    elevation: 1.5,
+  },
+  buttonRight: {
+    flex: 1,
+    marginLeft: 16,
+    elevation: 1.5
+  },
+  inUse: {
+    color: '#fff',
+    fontSize: 12,
+    paddingTop: 4,
+    paddingLeft: 8,
+    paddingRight: 8,
+    paddingBottom: 4,
+    borderRadius: 4,
+    backgroundColor: '#FF7A00'
+  },
+  updateTip: {
+    color: '#aaa',
+    fontSize: 10,
+    textAlign: 'center',
+    paddingBottom: 16
+  },
+  privateView: {
+    height: $vht(25),
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  privateText: {
+    color: '#FA5759'
+  }
+})