瀏覽代碼

Charge UI V3

vbea 2 年之前
父節點
當前提交
66cc55a019

+ 5 - 1
Strides-APP/app.json

@@ -26,5 +26,9 @@
     "termsUseUrl": "https://chargeco.global/terms-of-use",
     "privacyPolicyUrl": "https://chargeco.global/privacy-policy"
   },
-  "company": "Strides YTL Pte. Ltd."
+  "company": "Strides YTL Pte. Ltd.",
+  "v3": {
+    "summary": true,
+    "paymentMethod": true
+  }
 }

+ 11 - 1
Strides-APP/app/api/apiCharge.js

@@ -1,6 +1,7 @@
-import { get, post } from "./http";
+import { get, post, put } from "./http";
 
 const prefix = 'devicesApi/charge/';
+const prefixV2 = 'devicesApi/v2/';
 
 export default {
   //打开时校验用户是否在充电中
@@ -48,5 +49,14 @@ export default {
   },
   getConnectorDetail: (params) => {
     return get(prefix + 'getConnectorUsageDetail', params);
+  },
+  getPaymentTypeOptions: () => {
+    return get(prefix + "payment-methods")
+  },
+  setDefaultPaymentType: (data) => {
+    return post(prefix + "default-payment-methods", data)
+  },
+  startChargeV3: (data) => {
+    return post(prefixV2 + "charging/start", data)
   }
 }

+ 3 - 1
Strides-APP/app/components/BadgeSelectItem.js

@@ -11,7 +11,9 @@ const BadgeSelectItem = ({
   tintColor=colorAccent,
   borderColor="transparent"
 }) => (
-  <Pressable onPress={onPress} style={mergeStyle(style, checked ? tintColor : borderColor)}>
+  <Pressable 
+    onPress={onPress}
+    style={mergeStyle(style, checked ? tintColor : borderColor)}>
     {children}
     { checked &&
       <Svg 

+ 2 - 0
Strides-APP/app/components/Dialog.js

@@ -373,9 +373,11 @@ const andStyles = StyleSheet.create({
     flexDirection: 'row'
   },
   endView: {
+    width: 16,
     paddingTop: 16
   },
   halfView: {
+    width: 8,
     paddingTop: 8
   }
 });

+ 3 - 0
Strides-APP/app/i18n/locales/en.js

@@ -457,6 +457,7 @@ export default {
     chargingSessionComplete: "Your charging session is completed.",
     labelChargTransSubtotal: "Charging Transaction Subtotal (w 8% GST):",
     labelChargTransSubtotal2: "Charging Transaction Subtotal ($s):",
+    labelChargTransSubtotal3: "Charging Transaction Subtotal:",
     labelDateTime: "Date Time:",
     labelDurationReservation: "Duration of Reservation:",
     labelFinalPaymentAmount: "Final Payment Amount:",
@@ -464,11 +465,13 @@ export default {
     labelIdleDuration: "Idle Duration:",
     labelIdleFeeSubtotal: "Idle Fee Subtotal (w 8% GST):",
     labelIdleFeeSubtotal2: "Idle Fee Subtotal ($s):",
+    labelIdleFeeSubtotal3: "Idle Fee Subtotal:",
     labelIdleStartTime: "Idle Fee Start Time:",
     labelReferenceID: "Reference ID:",
     labelRegistrationNo: "Registration No:",
     labelReservationFeeSubtotal: "Reservation Fee Subtotal (w 8% GST):",
     labelReservationFeeSubtotal2: "Reservation Fee Subtotal ($s):",
+    labelReservationFeeSubtotal3: "Reservation Fee Subtotal:",
     labelSiteName: "Site Name:",
     labelTimeReservation: "Time of Reservation:",
     labelTransactionID: "Transaction ID:",

+ 3 - 0
Strides-APP/app/i18n/locales/zh-TW.js

@@ -457,6 +457,7 @@ export default {
     chargingSessionComplete: "您的充電結費已完成",
     labelChargTransSubtotal: "充電費用小計(含消費稅):",
     labelChargTransSubtotal2: "充電費用小計(含稅$s):",
+    labelChargTransSubtotal3: "充電費用小計:",
     labelDateTime: "交易時間",
     labelDurationReservation: "預定時長:",
     labelFinalPaymentAmount: "最終支付:",
@@ -464,11 +465,13 @@ export default {
     labelIdleDuration: "閑置時長:",
     labelIdleFeeSubtotal: "閑置費用小計(含消費稅):",
     labelIdleFeeSubtotal2: "閑置費用小計(含稅$s):",
+    labelIdleFeeSubtotal3: "閑置費用小計:",
     labelIdleStartTime: "閑置開始時間:",
     labelReferenceID: "引用編號:",
     labelRegistrationNo: "公司代碼:",
     labelReservationFeeSubtotal: "預訂費用小計(含消費稅):",
     labelReservationFeeSubtotal2: "預訂費用小計(含稅$s):",
+    labelReservationFeeSubtotal3: "預訂費用小計:",
     labelSiteName: "站點名稱:",
     labelTimeReservation: "預訂時間:",
     labelTransactionID: "交易編號:",

+ 3 - 0
Strides-APP/app/i18n/locales/zh.js

@@ -457,6 +457,7 @@ export default {
     chargingSessionComplete: "您的充电结费已完成",
     labelChargTransSubtotal: "充电费用小计(含消费税):",
     labelChargTransSubtotal2: "充电费用小计(含税$s):",
+    labelChargTransSubtotal3: "充电费用小计:",
     labelDateTime: "交易时间",
     labelDurationReservation: "预定时长:",
     labelFinalPaymentAmount: "最终支付:",
@@ -464,11 +465,13 @@ export default {
     labelIdleDuration: "闲置时长:",
     labelIdleFeeSubtotal: "闲置费用小计(含消费税):",
     labelIdleFeeSubtotal2: "闲置费用小计(含税$s):",
+    labelIdleFeeSubtotal3: "闲置费用小计:",
     labelIdleStartTime: "闲置开始时间:",
     labelReferenceID: "引用编号:",
     labelRegistrationNo: "公司代码:",
     labelReservationFeeSubtotal: "预订费用小计(含消费税):",
     labelReservationFeeSubtotal2: "预订费用小计(含税$s):",
+    labelReservationFeeSubtotal3: "预订费用小计:",
     labelSiteName: "站点名称:",
     labelTimeReservation: "预订时间:",
     labelTransactionID: "交易编号:",

+ 2 - 1
Strides-APP/app/pages/Router.js

@@ -26,6 +26,7 @@ import Privacy from './my/Privacy';
 import Profile from './my/ProfileV2';
 import Condition from './my/Condition';
 import Summary from './chargeV2/SummaryV2';
+import SummaryV3 from './chargeV2/SummaryV3';
 import Rating from './charge/Rating';
 import Wallet from './wallet/Wallet';
 import EditProfile from './my/EditProfile';
@@ -149,7 +150,7 @@ export var PageList = {
   'summary': {
     title: 'Summary',
     titleScope: 'receipt.receipt',
-    component: Summary
+    component: app.v3.summary ? SummaryV3 : Summary
   },
   'rating': {
     title: 'Your Rating',

+ 240 - 0
Strides-APP/app/pages/chargeV2/PaymentListV2.js

@@ -0,0 +1,240 @@
+/**
+ * 新版支付方式选择器
+ * @邠心vbe on 2021/04/13
+ */
+import React, { Component } from 'react';
+import { View, Text, StyleSheet, Pressable, ScrollView } from 'react-native';
+import BottomModal from '../../components/BottomModal';
+import BadgeSelectItem from '../../components/BadgeSelectItem';
+import { ChargeStyle } from './Charging';
+import { PAYTYPE, PaymentIcon } from '../payment/PaymentConfig';
+import TextView from '../../components/TextView';
+import apiCharge from '../../api/apiCharge';
+import Button from '../../components/Button';
+import Dialog from '../../components/Dialog';
+
+export default class PaymentListV2 extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      visible: false,
+      isSelect: true,
+      options: [],
+      selectIndex: 0,
+      selectOptions: {}
+    };
+  }
+
+  componentDidMount() {
+    this.getPaymentOptions();
+    this.setState({
+      isSelect: (this.props.isSelect || true)
+    })
+  }
+
+  componentDidUpdate() {
+    if (this.props.isSelect != undefined && this.state.isSelect !== this.props.isSelect) {
+      this.setState({
+        isSelect: this.props.isSelect
+      })
+    }
+  }
+
+  getPaymentOptions() {
+    apiCharge.getPaymentTypeOptions().then(res => {
+      if (res.data) {
+        this.setState({
+          options: res.data
+        })
+        if (this.props.payType && this.props.payType.code) {
+          for (let i = 0; i < res.data.length; i++) {
+            let type = res.data[i];
+            if (type.code == this.props.payType.code) {
+              this.changeMethod(i);
+              break;
+            }
+          }
+        } else {
+          let index = 0;
+          for (let i = 0; i < res.data.length; i++) {
+            let type = res.data[i];
+            if (type.def) {
+              index = i;
+              break;
+            }
+          }
+          this.changeMethod(index);
+        }
+      }
+    }).catch(err => {
+
+    })
+  }
+
+  showDialog(visible) {
+    if (this.state.isSelect) {
+      this.setState({
+        visible: visible
+      })
+    }
+  }
+
+  changeMethod(index) {
+    const type = this.state.options[index];
+    this.setState({
+      selectIndex: index,
+      selectOptions: type
+    }, () => {
+      if (this.props.onMethodChange) {
+        this.props.onMethodChange(this.state.selectOptions);
+      }
+    })
+  }
+
+  setDefault() {
+    const code = this.state.selectOptions?.code;
+    if (code && !this.state.selectOptions.def) {
+      Dialog.showProgressDialog();
+      apiCharge.setDefaultPaymentType({
+        defaultPaymentMethod: code
+      }).then(res => {
+        toastShort("success");
+        this.getPaymentOptions();
+      }).catch(err => {
+        toastShort(err)
+      }).finally(() => {
+        Dialog.dismissLoading();
+      })
+    }
+  }
+
+  render() {
+    return (
+      <View>
+        <BadgeSelectItem
+          style={ChargeStyle.stationInfoView}
+          checked={false}
+          onPress={() => this.showDialog(true)}>
+          <PaymentIcon
+            method={"WALLET"}
+            checked={true}/>
+          <View style={styles.paymentView}>
+            <TextView style={styles.valueText}>{this.state.selectOptions?.name}</TextView>
+            <TextView style={styles.paymentText}>{this.state.selectOptions?.desc}</TextView>
+          </View>
+          { this.state.selectOptions?.def &&
+            <TextView style={styles.textDefault}>Default</TextView>
+          }
+          { this.state.isSelect &&
+            <FontAwesome6
+              name={"angle-right"}
+              size={16}
+              color={colorCancel}
+            />
+          }
+        </BadgeSelectItem>
+        <BottomModal
+          visible={this.state.visible}
+          onHide={() => this.showDialog(false)}>
+          <View style={styles.dialogContent}>
+            <TextView style={styles.titleText}>Payment Methods</TextView>
+            <ScrollView style={styles.paymentList}>
+              { this.state.options.map((item, index) => (
+                <Pressable
+                  key={index}
+                  style={styles.itemPayment}
+                  onPress={() => this.changeMethod(index)}>
+                    <View style={ui.flex1}>
+                      <TextView style={styles.valueText}>{item.name}</TextView>
+                      <TextView style={styles.descText}>{item.desc}</TextView>
+                    </View>
+                    { item.def &&
+                      <TextView style={styles.tagDefault}>Default</TextView>
+                    }
+                    <MaterialCommunityIcons
+                      name={index == this.state.selectIndex ? "radiobox-marked" : "radiobox-blank"}
+                      size={24}
+                      color={index == this.state.selectIndex ? colorPrimary : textPrimary}
+                    />
+                  </Pressable>
+                ))
+              }
+            </ScrollView>
+            <EndView/><EndView/>
+            <View style={ui.flexc}>
+              <Button 
+                style={styles.defButton}
+                text={"Set Default"}
+                onClick={() => this.setDefault()}/>
+              <EndView half/>
+              <Button
+                style={ui.flex1}
+                text={"Confirm"}
+                onClick={() => this.showDialog(false)}/>
+            </View>
+          </View>
+        </BottomModal>
+      </View>
+    );
+  }
+}
+
+const styles = StyleSheet.create({
+  paymentView: {
+    flex: 1,
+    alignItems: 'center',
+    flexDirection: 'row',
+    justifyContent: 'space-around'
+  },
+  dialogContent: {
+    padding: 16
+  },
+  paymentList: {
+    height: $vh(50)
+  },
+  titleText: {
+    fontSize: 16,
+    fontWeight: 'bold',
+    paddingBottom: 18
+  },
+  valueText: {
+    color: textPrimary,
+    fontSize: 15,
+    fontWeight: 'bold'
+  },
+  paymentText: {
+    color: textSecondary,
+    fontSize: 12,
+    paddingLeft: 8,
+    paddingRight: 16
+  },
+  descText: {
+    color: colorAccent,
+    fontSize: 12,
+    paddingTop: 2
+  },
+  itemPayment: {
+    paddingTop: 8,
+    paddingBottom: 8,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  tagDefault: {
+    color: textLight,
+    fontSize: 12,
+    borderRadius: 4,
+    ...$padding(4, 8),
+    marginRight: 12,
+    backgroundColor: colorAccent
+  },
+  textDefault: {
+    color: colorAccent,
+    fontSize: 12,
+    marginRight: 8,
+    fontWeight: 'bold'
+  },
+  defButton: {
+    flex: 1,
+    backgroundColor: colorPrimary
+  }
+})

+ 465 - 0
Strides-APP/app/pages/chargeV2/SummaryV3.js

@@ -0,0 +1,465 @@
+/**
+ * V3新版充电结算页面
+ * @邠心vbe on 2023/12/22
+ */
+import React, { Component } from 'react';
+import { View, Text, StyleSheet, ScrollView, RefreshControl } from 'react-native';
+import apiCharge from '../../api/apiCharge';
+import Button from '../../components/Button';
+import Dialog from '../../components/Dialog';
+import { MyRefreshProps } from '../../components/ThemesConfig';
+import utils from '../../utils/utils';
+import { PageList } from '../Router';
+import app from '../../../app.json';
+import TextView from '../../components/TextView';
+
+export default class Summary extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      isActully: true,
+      refreshing: false,
+      summaryInfo: {
+        top: {},
+        station: {},
+        connector: {},
+        chargingFee: {},
+        idleFee: {},
+        reservationFee: {},
+        payment: {}
+      },
+      chargingPk: "",
+      isPendding: false
+    };
+    this.action = "";
+    this.canBack = false;
+  }
+
+  componentDidMount() {
+    const params = this.props.route.params;
+    if (params.chargingPk) {
+      Dialog.showProgressDialog();
+      this.setState({
+        chargingPk: params.chargingPk
+      })
+      if (params.action && params.action == "view") {
+        this.setState({
+          isActully: false
+        });
+        this.getSummaryData(params.chargingPk);
+      } else {
+        setTimeout(() => {
+          this.getSummaryData(params.chargingPk);
+        }, 1500);
+      }
+    }
+    if (params.action) {
+      this.action = params.action;
+    }
+    this.props.navigation.addListener('focus', e => {
+      this.canBack = false;
+    });
+    this.props.navigation.addListener('beforeRemove', e => {
+      if (this.state.isActully && !this.canBack) {
+        this.toRating();
+        e.preventDefault();
+      }
+    });
+  }
+
+  getSummaryData(chargingPk) {
+    apiCharge.getChargeSummaryV2({
+      chargingPk: chargingPk
+    }).then(res => {
+      Dialog.dismissLoading();
+      if (res.data) {
+        this.setState({
+          refreshing: false,
+          summaryInfo: res.data,
+          isPendding: (res.data.hasSettled != true)
+        });
+      }
+    }).catch((err) => {
+      Dialog.dismissLoading();
+      toastShort(err);
+      this.setState({
+        isPendding: true,
+        refreshing: false
+      });
+    });
+  }
+
+  onRefresh() {
+    if (this.state.chargingPk) {
+      this.setState({
+        refreshing: true
+      });
+      this.getSummaryData(this.state.chargingPk);
+    }
+  }
+
+  toRating() {
+    this.canBack = true;
+    if (this.action == "view") {
+      goBack();
+    } else {
+      //routeUtil.resetToHome(this.props);
+      startPage(PageList.home);
+      //startPage(PageList.rating, this.state.stationInfo);
+    }
+  }
+
+  toTransaction() {
+    if (this.action == "view") {
+      goBack();
+    } else {
+      startPage(PageList.wallet)
+    }
+  }
+
+  getSummaryText(data) {
+    if (this.state.summaryInfo?.paymentType == 'Fleet Credit') {
+      return '-';
+    } else {
+      return currency + '' + (data ?? '0');
+    }
+  }
+
+  getTaxTitle(title="") {
+    if (this.state.summaryInfo?.taxRate) {
+      return title.replace("$s", this.state.summaryInfo.taxRate)
+    } else {
+      return title;
+    }
+  }
+
+  render() {
+    if (this.state.isPendding) {
+      return (
+        <View style={styles.container}>
+          <View style={styles.processContent}>
+            <TextView style={styles.processTitle}>{$t("receipt.processTitle")}</TextView>
+            <TextView style={styles.processText}>{$t("receipt.processMessage1")}</TextView>
+            <TextView style={styles.processText}>
+              {$t("receipt.processMessage2")}
+              <Text style={[ui.bold, ui.underline]} onPress={() => this.toTransaction()}>{$t("receipt.processMessage3")}</Text>.
+            </TextView>
+            <TextView style={styles.processText}>{$t("receipt.processMessage4")}</TextView>
+          </View>
+          <View style={styles.bottomButton}>
+            <Button
+              text={$t('home.done')}
+              elevation={1.5}
+              onClick={() => this.toRating()}/>
+          </View>
+        </View>
+      )
+    } else {
+      return (
+        <ScrollView
+          style={styles.container}
+          refreshControl={
+            <RefreshControl
+              {...MyRefreshProps()}
+              refreshing={this.state.refreshing}
+              onRefresh={() => this.onRefresh()}
+            />
+          }>
+          { utils.isNotEmpty(this.state.summaryInfo.top) &&
+            <View style={styles.headerView}>
+              { this.state.isActully && <>
+                <Octicons
+                  name="check-circle-fill"
+                  color={colorAccent}
+                  size={56}/>
+                <TextView style={styles.topTitle}>{$t('receipt.successful')}</TextView>
+                <TextView style={styles.topDesc}>{$t('receipt.chargingSessionComplete')}</TextView>
+              </>}
+              { utils.isNotEmpty(this.state.summaryInfo.top.company) &&
+                <View style={styles.formRow}>
+                  <TextView style={styles.label}>{$t('sign.labelCompany')}:</TextView>
+                  <TextView style={styles.text}>{this.state.summaryInfo.top.company}</TextView>
+                </View>
+              }
+              { utils.isNotEmpty(this.state.summaryInfo.top.registrationNo) &&
+                <View style={styles.formRow}>
+                  <TextView style={styles.label}>{$t('receipt.labelRegistrationNo')}</TextView>
+                  <TextView style={styles.text}>{this.state.summaryInfo.top.registrationNo}</TextView>
+                </View>
+              }
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelTransactionID')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.top.transactionId}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelReferenceID')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.top.referenceId}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelDateTime')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.top.dateTime}</TextView>
+              </View>
+            </View>
+          }
+          { utils.isNotEmpty(this.state.summaryInfo.station) &&
+            <View style={styles.sections}>
+              <TextView style={styles.formTitle}>{$t('wallet.labelYourStation')}</TextView>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('wallet.labelStationId')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.station.stationId}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelSiteName')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.station.siteName ?? "-"}</TextView>
+              </View>
+            </View>
+          }
+          { utils.isNotEmpty(this.state.summaryInfo.connector) &&
+            <View style={styles.sections}>
+              <TextView style={styles.formTitle}>{$t('wallet.labelYourConnector')}</TextView>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('charging.labelType')}:</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.connector.type}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('charging.labelPower')}:</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.connector.power}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('charging.labelRates')}:</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.connector.rates}</TextView>
+              </View>
+            </View>
+          }
+          { utils.isNotEmpty(this.state.summaryInfo.chargingFee) &&
+            <View style={styles.sections}>
+              <TextView style={styles.formTitle}>{$t('receipt.breakdownChargingFees')}</TextView>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('wallet.labelChargeTime')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.chargingFee.chargeTime ?? "-"}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('wallet.labelChargeDelivered')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.chargingFee.chargeDelivered ?? 0}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelChargTransSubtotal3')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.chargingFee.transactionSubtotal}</TextView>
+              </View>
+            </View>
+          }
+          { utils.isNotEmpty(this.state.summaryInfo.idleFee) &&
+            <View style={styles.sections}>
+              <TextView style={styles.formTitle}>{$t('receipt.breakdownIdlesFees')}</TextView>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelIdleStartTime')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.idleFee.startTime}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelIdleDuration')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.idleFee.duration}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelIdleFeeSubtotal3')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.idleFee.subtotal}</TextView>
+              </View>
+            </View>
+          }
+          { utils.isNotEmpty(this.state.summaryInfo.reservationFee) &&
+            <View style={styles.sections}>
+              <TextView style={styles.formTitle}>{$t('receipt.breakdownReservationFees')}</TextView>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelTimeReservation')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.reservationFee.reservationTime}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelDurationReservation')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.reservationFee.reservationDuration}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('receipt.labelReservationFeeSubtotal3')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.reservationFee.reservationFeeSubtotal}</TextView>
+              </View>
+            </View>
+          }
+          { utils.isNotEmpty(this.state.summaryInfo.payment) &&
+            <View style={styles.sections}>
+              <TextView style={styles.formTitle}>{$t('receipt.breakdownPayment')}</TextView>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('wallet.labelPaymentMadeBy')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.paymentMadeBy ?? "-"}</TextView>
+              </View>
+              { utils.isNotEmpty(this.state.summaryInfo.payment.transactionSubtotal) &&
+                <View style={styles.formRow}>
+                  <TextView style={styles.label}>{$t('receipt.labelChargTransSubtotal3')}</TextView>
+                  <TextView style={styles.text}>{this.state.summaryInfo.payment.transactionSubtotal}</TextView>
+                </View>
+              }
+              { utils.isNotEmpty(this.state.summaryInfo.payment.idleFeeSubtotal) &&
+                <View style={styles.formRow}>
+                  <TextView style={styles.label}>{$t('receipt.labelIdleFeeSubtotal3')}</TextView>
+                  <TextView style={styles.text}>{this.state.summaryInfo.payment.idleFeeSubtotal}</TextView>
+                </View>
+              }
+              { utils.isNotEmpty(this.state.summaryInfo.payment.reservationFeeSubtotal) &&
+                <View style={styles.formRow}>
+                  <TextView style={styles.label}>{$t('receipt.labelReservationFeeSubtotal3')}</TextView>
+                  <TextView style={styles.text}>{this.state.summaryInfo.payment.reservationFeeSubtotal}</TextView>
+                </View>
+              }
+              { utils.isNotEmpty(this.state.summaryInfo.payment.finalPayment) &&
+                <View style={styles.formRow}>
+                  <TextView style={styles.label}>
+                    { utils.isNotEmpty(this.state.summaryInfo.payment.discountCredit)
+                    ? $t('receipt.labelAfterDiscount')
+                    : $t('receipt.labelFinalPaymentAmount')
+                    }
+                  </TextView>
+                  <TextView style={styles.text}>{this.state.summaryInfo.payment.finalPayment ?? "-"}</TextView>
+                </View>
+              }
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('wallet.labelExchangeRate')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.exchangeRate ?? "-"}</TextView>
+              </View>
+              <View style={styles.formRow}>
+                <TextView style={styles.label}>{$t('wallet.labelResultingBalance')}</TextView>
+                <TextView style={styles.text}>{this.state.summaryInfo.payment.resultingBalance ?? "-"}</TextView>
+              </View>
+            </View>
+          }
+          { (this.state.isActully && utils.isNotEmpty(this.state.summaryInfo.top)) &&
+            <View style={styles.bottomButton}>
+              <TextView
+                style={styles.feedback}
+                onPress={() => startPage(PageList.feedback)}>{$t('wallet.linkSubmitFeedback')}</TextView>
+              {/* <Text style={styles.tipText}>{$t('wallet.tipsReceipt')}</Text> */}
+              <Button
+                text={$t('home.done')}
+                elevation={1.5}
+                onClick={() => this.toRating()}/>
+            </View>
+          }
+        </ScrollView>
+      );
+    }
+  }
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    paddingLeft: 16,
+    paddingRight: 16,
+    backgroundColor: colorLight
+  },
+  headerView: {
+    alignItems: 'center',
+    paddingTop: 16,
+    paddingBottom: 24
+  },
+  topTitle: {
+    color: textPrimary,
+    fontSize: 16,
+    marginTop: 8,
+    paddingLeft: 8,
+    fontWeight: 'bold'
+  },
+  topDesc: {
+    color: textPrimary,
+    fontSize: 14,
+    marginBottom: 32
+  },
+  sections: {
+    borderRadius: 10,
+    marginBottom: 12,
+    backgroundColor: colorLight
+  },
+  sections2: {
+    paddingTop: 0,
+    paddingLeft: 0,
+    paddingRight: 0,
+    paddingBottom: 8,
+    borderRadius: 10,
+    marginBottom: 8,
+    backgroundColor: colorLight
+  },
+  formTitle: {
+    color: '#000',
+    fontSize: 14,
+    marginTop: -4,
+    marginBottom: 6,
+    paddingBottom: 6,
+    fontWeight: 'bold',
+    borderBottomWidth: 1,
+    borderBottomColor: textPrimary
+  },
+  formRow: {
+    paddingTop: 3,
+    paddingBottom: 3,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  label: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 13,
+  },
+  text: {
+    color: textPrimary,
+    fontSize: 12,
+    fontWeight: 'bold'
+  },
+  stationInfoView: {
+    padding: 12,
+    marginBottom: 0,
+    alignItems: 'center',
+    flexDirection: 'row',
+    justifyContent: 'space-between'
+  },
+  stationInfoText: {
+    color: '#999',
+    fontSize: 11
+  },
+  connectorView: {
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  typeIcon: {
+    width: 36,
+    height: 36
+  },
+  feedback: {
+    color: '#12A5F9',
+    fontSize: 14,
+    textAlign: 'center',
+    marginBottom: 16,
+    ...ui.underline
+  },
+  bottomButton: {
+    marginTop: 32,
+    marginBottom: 16
+  },
+  tipText: {
+    color: textPrimary,
+    fontSize: 12,
+    textAlign: 'center',
+    marginBottom: 8
+  },
+  processContent: {
+    flex: 1,
+    justifyContent: 'center'
+  },
+  processTitle: {
+    color: textTitle,
+    fontSize: 24,
+    fontWeight: 'bold',
+    textAlign: 'center'
+  },
+  processText: {
+    color: textPrimary,
+    fontSize: 14,
+    textAlign: 'center',
+    paddingTop: 16
+  }
+});

+ 58 - 3
Strides-APP/app/pages/chargingV2/ChargingPage.js

@@ -4,6 +4,7 @@
  */
 import React, { Component } from 'react';
 import { View } from 'react-native';
+import app from '../../../app.json';
 import apiCharge from '../../api/apiCharge';
 import apiWallet from '../../api/apiWallet';
 import Dialog from '../../components/Dialog';
@@ -105,7 +106,10 @@ export default class ChargingPage extends Component {
       sitePk: this.state.stationInfo.id,
       chargeBoxId: this.state.stationInfo.chargeBoxId,
       connectorId: this.state.stationInfo.connectorId,
-      paymentOption: this.state.currentPayment
+      paymentOption: this.state.currentPayment,
+    }
+    if (app.v3.paymentMethod && this.state.currentPayment?.code) {
+      params.paymentMethod = this.state.currentPayment?.code
     }
     console.log("参数", params);
     apiCharge.getConnectorDetail(params).then(res => {
@@ -117,7 +121,13 @@ export default class ChargingPage extends Component {
           connectorInfo: {}
         }
         state.connectorInfo = res.data;
-        if (res.data.currentPaymentType && res.data.currentPaymentType == PAYTYPE.PAY_PER_USE) {
+        if (app.v3.paymentMethod && res.data.currentPaymentMethod) {
+          //V3版获取当前支付方式
+          state.currentPayment = {
+            code: res.data.currentPaymentMethod
+          }
+        } else if (res.data.currentPaymentType && res.data.currentPaymentType == PAYTYPE.PAY_PER_USE) {
+          //V2版获取当前支付方式
           state.currentPayment = PAYTYPE.PAY_PER_USE
         }
         console.log("状态", res.data.status);
@@ -247,7 +257,11 @@ export default class ChargingPage extends Component {
       isCharging: true
     });
     this.waitStartCharging = true;
-    if (this.state.currentPayment == PAYTYPE.PAY_PER_USE) {
+    if (app.v3.paymentMethod) { //V3版本开始充电
+      this.onStartChargeV3();
+      return;
+    }
+    if (this.state.currentPayment == PAYTYPE.PAY_PER_USE) { //V2版本PayPerUse
       this.onStartChargePerUse();
       return;
     }
@@ -300,6 +314,47 @@ export default class ChargingPage extends Component {
     });
   }
 
+  onStartChargeV3() {
+    const params = {
+      sitePk: this.state.stationInfo.id,
+      chargeBoxId: this.state.stationInfo.chargeBoxId,
+      connectorId: this.state.stationInfo.connectorId
+    }
+    if (this.state.currentPayment?.code) {
+      params.paymentMethod = this.state.currentPayment?.code
+    }
+    console.log("[开始充电V3-params]", params);
+    apiCharge.startChargeV3(params).then(res => {
+      console.log("[开始充电V3-response]", res);
+      if (res.data.webPaymentUrl) {
+        this.setState({
+          currentPerUse: "Pending"
+        })
+        startPage(PageList.paymentWeb, { amount: params.sitePk, url: res.data.webPaymentUrl, type: 'PayPerUse' });
+      } else {
+        setTimeout(() => {
+          this.canAutoRefresh = true;
+          this.refreshChargeData(500);
+          if (res.msg) {
+            toastShort(res.msg)
+          }
+        }, 3000);
+      }
+    }).catch(({err, code, data}) => {
+      //toastShort(err);
+      console.log("[开始充电V3-错误]", err, code);
+      //Dialog.dismissLoading();
+      if (code == 5200) {
+        this.showErrorDialog('none', "(" + data.transactionPk + ') ' + err);
+      } else {
+        this.showErrorDialog('A4', err);
+      }
+      this.setState({
+        isCharging: false
+      });
+    });
+  }
+
   onStopCharge() {
     Dialog.showDialog({
       title: $t('charging.titleStopCharging'),

+ 14 - 2
Strides-APP/app/pages/chargingV2/StepAuth.js

@@ -4,9 +4,11 @@
  */
 import React, { useEffect, useState } from 'react';
 import { View, Text, Image, StyleSheet } from 'react-native';
+import app from '../../../app.json';
 import Button from '../../components/Button';
 import TextView from '../../components/TextView';
 import { PaymentList } from '../chargeV2/Payment';
+import PaymentListV2 from '../chargeV2/PaymentListV2';
 
 export default StepAuth = ({
   status="",
@@ -66,10 +68,20 @@ export default StepAuth = ({
       </View>
       <View style={styles.bottomView}>
         <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
-        <PaymentList
+        {/* <PaymentList
           payType={currentPayment}
           onMethodChange={onPaymentMethodChanged}
-        />
+        /> */}
+        { app.v3.paymentMethod
+        ? <PaymentListV2
+            isSelect={isAuthentic}
+            payType={currentPayment}
+            onMethodChange={onPaymentMethodChanged}/>
+        : <PaymentList
+            isSelect={isAuthentic}
+            payType={currentPayment}
+            onMethodChange={onPaymentMethodChanged}/>
+        }
         { isAuthentic
         ? <Button
             style={styles.buttonView}

+ 17 - 8
Strides-APP/app/pages/chargingV2/StepCharging.js

@@ -10,6 +10,7 @@ import TextView from '../../components/TextView';
 import utils from '../../utils/utils';
 import { PaymentList } from '../chargeV2/Payment';
 import DiscountView from './DiscountView';
+import PaymentListV2 from '../chargeV2/PaymentListV2';
 
 const StepCharging = ({
   connectorInfo={},
@@ -158,10 +159,14 @@ const StepCharging = ({
       </Animated.View>
       <EndView/>
       <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
-      <PaymentList
-        isSelect={false}
-        payType={currentPayment}
-      />
+      { app.v3.paymentMethod
+      ? <PaymentListV2
+          isSelect={false}
+          payType={currentPayment}/>
+      : <PaymentList
+          isSelect={false}
+          payType={currentPayment}/>
+      }
       <Button
         style={styles.buttonView}
         text={$t('charging.btnStopCharging')}
@@ -187,10 +192,14 @@ const StepCharging = ({
       </Animated.View>
       <View style={styles.bottomView}>
         <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
-        <PaymentList
-          isSelect={false}
-          payType={currentPayment}
-        />
+        { app.v3.paymentMethod
+        ? <PaymentListV2
+            isSelect={false}
+            payType={currentPayment}/>
+        : <PaymentList
+            isSelect={false}
+            payType={currentPayment}/>
+        }
         <View style={styles.buttonView}></View>
       </View>
     </View>

+ 9 - 4
Strides-APP/app/pages/chargingV2/StepStart.js

@@ -10,6 +10,7 @@ import TextView from '../../components/TextView';
 import { ChargeStyle } from '../chargeV2/Charging';
 import { PaymentList } from '../chargeV2/Payment';
 import DiscountView from './DiscountView';
+import PaymentListV2 from '../chargeV2/PaymentListV2';
 
 export default StepStart = ({
   connectorInfo={},
@@ -77,10 +78,14 @@ export default StepStart = ({
       <EndView/>
       <EndView half/>
       <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
-      <PaymentList
-        payType={currentPayment}
-        onMethodChange={onPaymentMethodChanged}
-      />
+      { app.v3.paymentMethod
+      ? <PaymentListV2
+          payType={currentPayment}
+          onMethodChange={onPaymentMethodChanged}/>
+      : <PaymentList
+          payType={currentPayment}
+          onMethodChange={onPaymentMethodChanged}/>
+      }
       <Button
         style={styles.buttonView}
         text={$t('charging.btnAuthenticate')}

+ 3 - 3
Strides-APP/app/pages/chargingV2/StepStop.js

@@ -47,14 +47,14 @@ export default StepStop = ({
         <TextView style={styles.stepDesc}>{$t('charging.stepStoppingChargeDesc')}</TextView>
       </View>
 
-      <View style={styles.bottomView}>
-      <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
+      {/* <View style={styles.bottomView}>
+        <TextView style={styles.label}>{$t('charging.paymentMethod')}</TextView>
         <PaymentList
           isSelect={false}
           payType={currentPayment}
         />
         <View style={{height: 56}}/>
-      </View>
+      </View> */}
     </View>
   )
 }

+ 4 - 2
Strides-APP/app/pages/wallet/Overview.js

@@ -115,9 +115,11 @@ export default class Overview extends Component {
         this.setState({
           skeleton: false,
           chartReady: true
+        }, () => {
+          this.stopRefresh();
         })
       });
-      this.stopRefresh();
+      
     }).catch(err => {
       toastShort(err);
       this.stopRefresh();
@@ -131,7 +133,7 @@ export default class Overview extends Component {
     this.refreshing = false;
     setTimeout(() => {
       Dialog.dismissLoading();
-    }, 300);
+    }, 1000);
   }
 
   barTheme = (active) => ({