Переглянути джерело

Add a new manual input "Station ID" Dialog at "QR Scan" menu
https://dev.wormwood.com.sg/zentao/task-view-918.html
Develop Delete Account Logic
https://dev.wormwood.com.sg/zentao/task-view-921.html

wudebin 3 місяців тому
батько
коміт
feca6b837e

+ 17 - 3
Strides-APP/app/pages/Router.js

@@ -78,6 +78,7 @@ import RegisterVL from './signLumi/RegisterVL';
 import ForgotPwdVL from './signLumi/ForgotPwdVL';
 import Wallets from './wallets/Wallets';
 import utils from '../utils/utils';
+import DeleteAccount from './my/DeleteAccount';
 
 export var PageList = {
   'splash': {
@@ -124,8 +125,8 @@ export var PageList = {
     component: ChargeDetails
   },
   'chargeDetailPage': {
-    title: 'Charging Site',
-    titleScope: 'route.chargingSite',
+    //title: 'Charging Site',
+    //titleScope: 'route.chargingSite',
     component: ChargeAdapter
   },
   'chargingPage': {
@@ -414,6 +415,11 @@ export var PageList = {
     titleScope: 'route.pointsHistory',
     component: PointsHistory
   },
+  'deleteAccount': {
+    title: "Delete Account",
+    titleScope: 'profile.deleteAccount',
+    component: DeleteAccount
+  },
   'settings': {
     title: 'Settings',
     titleScope: 'route.settings',
@@ -476,7 +482,8 @@ const Title = (title, opt = {}, titleScope) => {
     options.header = () => <Toolbar scope={titleScope} rightIcon={options?.headerRight}/>
   } else {
     options.title = title;
-    options.headerLeft = () => <BackButton/>
+    options.header = () => <Toolbar scope={titleScope} rightIcon={options?.headerRight}/>
+    //options.headerLeft = () => <BackButton/>
   }
   return options;
 }
@@ -534,6 +541,13 @@ const Router = () => {
         startPage(PageList.home);
       }
     }
+    global.goBack2 = () => {
+      if (navigation.current?.canGoBack()) {
+        navigation.current?.goBack();
+      } else {
+        startPage(PageList.home);
+      }
+    }
     return (() => {
       global.startPage = null;
       global.goBack = null;

+ 2 - 2
Strides-APP/app/pages/charge/Charge.js

@@ -10,7 +10,7 @@ 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 QRResult from './QRResult';
 import { ErrorDialog } from './InfoDialog';
 import utils from '../../utils/utils';
 import TextView from '../../components/TextView';
@@ -648,7 +648,7 @@ export default class Charge extends Component {
       <View style={{
           paddingLeft: 16,
           paddingRight: 16,
-          ...this.props.style
+          ...(this.props.style || {})
         }}>
         { this.state.isAuthentic //是否扫码认证
           ? this.state.isStart   //是否开始充电

+ 1 - 1
Strides-APP/app/pages/charge/Charging.js

@@ -9,7 +9,7 @@ import Modal from 'react-native-modal';
 import { ModalProps } from '../../components/BottomModal';
 import ChargeItemSelect from '../../icons/ChargeItemSelect';
 import { DialogMaxWidth } from './InfoDialog';
-import { QRResult } from './QRScan';
+import QRResult from './QRResult';
 
 export const circleSize = $vw(50.66) < 300 ? $vw(50.66) : 300;
 

+ 1 - 1
Strides-APP/app/pages/charge/Details.js

@@ -14,7 +14,7 @@ import Charge from './Charge';
 import Explore from './Explore';
 import { LoginDialog } from './InfoDialog';
 import Provider from './Provider';
-import { QRResult } from './QRScan';
+import QRResult from './QRResult';
 import Reserve from './Reserve';
 import SiteInfo from './SiteInfo';
 

+ 70 - 0
Strides-APP/app/pages/charge/QRResult.js

@@ -0,0 +1,70 @@
+import apiCharge from "../../api/apiCharge";
+import Dialog from "../../components/Dialog";
+import { PageList } from "../Router";
+
+export default {
+  haveResult() {
+    return global.QrCodeResult && global.QrCodeResult.connectorPk;
+  },
+  setResult(info) {
+    global.QrCodeResult = info;
+  },
+  getResult() {
+    return global.QrCodeResult;
+  },
+  clearResult() {
+    global.QrCodeResult = {};
+  },
+  applyInputStation(text, sitePk, back) {
+    if (text.indexOf('-') > 0) {
+      const arr = text.split('-');
+      if (arr.length >= 2) {
+        let bid = '', cid = '';
+        for (let i = 0; i < arr.length; i++) {
+          let sb = arr[i]?.trim();
+          if (i == (arr.length-1)) {
+            cid = sb;
+          } else {
+            if (i > 0) {
+              bid += '-';
+            }
+            bid += sb;
+          }
+        }
+        const qr = {
+          sitePk: sitePk,
+          chargeBoxId: bid,
+          connectorId: cid
+        }
+        console.log('====================================');
+        console.log(qr);
+        console.log('====================================');
+        Dialog.showProgressDialog();
+        apiCharge.checkQRStatus(qr).then(res => {
+          Dialog.dismissLoading();
+          if (res.data && res.data.chargeBoxId) {
+            this.setResult(res.data);
+            back(true)
+          }
+        }).catch(({err, code}) => {
+          Dialog.dismissLoading();
+          back(false, '')
+          Dialog.showDialog({
+            title: 'Error',
+            message: err,
+            showCancel: false,
+            callback: btn => {
+              if (code == 5194 && btn == Dialog.BUTTON_OK) {
+                startPage(PageList.myVehicles);
+              }
+            }
+          });
+        })
+      } else {
+        back(false, 'Station ID is incorrect');
+      }
+    } else {
+      back(false, 'Station ID is incorrect');
+    }
+  }
+}

+ 54 - 27
Strides-APP/app/pages/chargeV2/ChargeAdapter.js

@@ -4,19 +4,20 @@
  */
 import React, { Component } from 'react';
 import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
-import { BackHandler, Pressable, StyleSheet } from 'react-native';
+import { View, BackHandler, Pressable, StyleSheet } from 'react-native';
 import TabInfos from './TabInfos';
 import TabCharge from './TabCharge';
 import Reserve from './TabReserve';
 import TabInfosV3 from '../chargeV3/TabInfos';
-import { QRResult } from '../charge/QRScan';
+import QRResult from '../charge/QRResult';
 import apiStation from '../../api/apiStation';
 import PagerUtil from './PagerUtil';
 import utils from '../../utils/utils';
-import { Styles } from '../../components/Toolbar';
+import Toolbar, { Styles } from '../../components/Toolbar';
 import { getFocusedRouteNameFromRoute } from '@react-navigation/core';
 import app from '../../../app.json';
 import HeaderTitle from '../../components/HeaderTitle';
+import { PageList } from '../Router';
 
 export const PagerList = {
   "tabInfo": "Info",
@@ -63,6 +64,7 @@ export default class ChargeAdapter extends Component {
     this.action = "";
     this.isHide = false;
     this.titleName = "";
+    this.backHandler = undefined;
   }
 
   componentDidMount() {
@@ -91,7 +93,7 @@ export default class ChargeAdapter extends Component {
         this.canShowLoginDialog();
       });
     }
-    BackHandler.addEventListener('hardwareBackPress', this.backPage)
+    this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.backPage)
     PagerUtil.addOnRefresh(this);
     PagerUtil.setSelectedVoucher({});
     // setTimeout(() => {
@@ -116,13 +118,19 @@ export default class ChargeAdapter extends Component {
   componentWillUnmount() {
     QRResult.clearResult();
     PagerUtil.onDestory();
-    BackHandler.removeEventListener("hardwareBackPress", this.backPage)
+    if (this.backHandler) {
+      this.backHandler.remove();
+    }
   }
 
   backPage = () => {
     const params = this.props.route.params;
     if (params.from && !this.isHide) {
-      startPage(params.from);
+      if (params.from == PageList.home) {
+        startPage(params.from);
+      } else {
+        goBack2();
+      }
       return true;
     }
   }
@@ -257,33 +265,52 @@ export default class ChargeAdapter extends Component {
   render() {
     const Tab = createMaterialTopTabNavigator();
     return (
-      <Tab.Navigator
-        style={styles.container}
-        screenOptions={{
-          lazy: false,
-          lazyPreloadDistance: 1,
-          ...this.tabBarStyle
-        }}
-        backBehavior={() => this.backPage()}
-        initialRouteName={PagerList.tabCharge}>
-        { this.pageAdapter.map((item, index) => 
-          <Tab.Screen
-            key={index}
-            name={item.name}
-            component={item.component}
-            options={{
-              title: item.title,
-              tabBarAllowFontScaling: false
-            }}
-          />
-        )}
-      </Tab.Navigator>
+      <View style={styles.container}>
+        <Toolbar
+          title={this.state.stationInfo?.name ?? "Charging Site"}
+          rightIcon={() => (
+            <Pressable
+              style={Styles.backIcon}
+              android_ripple={rippleLessIcon}
+              onPress={() => utils.directMaps(this.state.stationInfo.latitude, this.state.stationInfo.longitude, this.state.stationInfo.address)}>
+              <MaterialIcons
+                name='directions'
+                size={24}
+                color={pageTitleTint}
+                style={Styles.iconOpacity}
+              />
+            </Pressable>
+          )}
+        />
+        <Tab.Navigator
+          style={styles.container}
+          screenOptions={{
+            lazy: false,
+            lazyPreloadDistance: 1,
+            ...this.tabBarStyle
+          }}
+          backBehavior={() => this.backPage()}
+          initialRouteName={PagerList.tabCharge}>
+          { this.pageAdapter.map((item, index) => 
+            <Tab.Screen
+              key={index}
+              name={item.name}
+              component={item.component}
+              options={{
+                title: item.title,
+                tabBarAllowFontScaling: false
+              }}
+            />
+          )}
+        </Tab.Navigator>
+      </View>
     );
   }
 }
 
 const styles = StyleSheet.create({
   container: {
+    flex: 1,
     backgroundColor: colorLight
   },
   tabStyle: {

+ 1 - 1
Strides-APP/app/pages/chargeV2/Charging.js

@@ -10,7 +10,7 @@ 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 QRResult from '../charge/QRResult';
 import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg';
 import utils from '../../utils/utils';
 import TextView from '../../components/TextView';

+ 1 - 1
Strides-APP/app/pages/chargeV2/TabCharge.js

@@ -8,7 +8,7 @@ import apiCharge from '../../api/apiCharge';
 import Dialog from '../../components/Dialog';
 import { PageList } from '../Router';
 import { EnterStationDialog } from './Charging';
-import { QRResult } from '../charge/QRScan';
+import QRResult from '../charge/QRResult';
 import { ErrorDialog } from './InfoDialog';
 import PagerUtil from './PagerUtil';
 import StepStartView from '../charging/StepStartView';

+ 1 - 1
Strides-APP/app/pages/chargeV2/TabReserve.js

@@ -14,7 +14,7 @@ import PagerUtil from './PagerUtil';
 import BadgeSelectItem from '../../components/BadgeSelectItem';
 import TextView from '../../components/TextView';
 import { MyRefreshProps } from '../../components/ThemesConfig';
-import { QRResult } from '../charge/QRScan';
+import QRResult from '../charge/QRResult';
 import { PagerList } from './ChargeAdapter';
 
 export default class TabReserve extends Component {

+ 1 - 1
Strides-APP/app/pages/chargeV3/ChargingStartView.js

@@ -18,7 +18,7 @@ export default ChargingStartView = ({
   onIntoCharging
 }) => (
   <View>
-    <View style={{minHeight: $vht(80)}}>
+    <View style={{minHeight: $vht(70)}}>
       <EndView/>
       <View style={ui.flexcc}>
         <MaterialIcons

+ 1 - 1
Strides-APP/app/pages/charging/StepStartView.js

@@ -18,7 +18,7 @@ export default StepStartView = ({
   onIntoCharging
 }) => (
   <View>
-    <View style={{minHeight: $vht(80)}}>
+    <View style={{minHeight: $vht(70)}}>
       <TextView style={styles.gstText}>{hideTaxRates ? $t('charging.tipsRatesTax2') : $t('charging.tipsRatesTax')}</TextView>
       <TextView style={styles.title}>{$t('charging.acChargers')} ({(stationInfo?.acConnector?.available ?? "0") + $t('charging.numberAvailable')})</TextView>
       { stationInfo.acRates?.length > 0

+ 180 - 0
Strides-APP/app/pages/my/DeleteAccount.js

@@ -0,0 +1,180 @@
+/**
+ * 删除账号页面
+ * @邠心vbe on 2025/06/27
+ */
+import React, { Component } from 'react';
+import { View, StyleSheet, ScrollView } from 'react-native';
+import CheckBoxText from '../../components/CheckBoxText';
+import Button from '../../components/Button';
+import apiUser from '../../api/apiUser';
+import Dialog from '../../components/Dialog';
+import { PageList } from '../Router';
+import TextView from '../../components/TextView';
+import { getStorageJsonSync, setStorage, setStorageJson } from '../../utils/storage';
+import { setAccessToken } from '../../api/http';
+
+export default class DeleteAccount extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      checkValue1: false,
+      checkValue2: false,
+      checkValue3: false
+    };
+  }
+
+  deleteAccount() {
+    Dialog.showDialog({
+      title: $t('profile.deleteAccount'),
+      message: $t('profile.confirmDeleteAccount'),
+      ok: $t('nav.confirm'),
+      callback: button => {
+        if (button == Dialog.BUTTON_OK) {
+          this.deleteMyAccount();
+        }
+      }
+    })
+  }
+
+  deleteMyAccount(again=false) {
+    Dialog.showProgressDialog();
+    apiUser.deleteAccount({
+      secondConfirm: again
+    }).then(res => {
+      toastShort($t('profile.deleteAccountSuccess'))
+      Dialog.dismissLoading();
+      this.requestLogout();
+      /*setTimeout(() => {
+        startPage(PageList.login);
+      }, 500);*/
+    }).catch(err => {
+      Dialog.dismissLoading();
+      //toastShort(err)
+      setTimeout(() => {
+        if (err.code == 5334) {
+          Dialog.showDialog({
+            title: $t('profile.deleteAccount'),
+            message: err.msg,
+            ok: $t("nav.confirm"),
+            callback: button => {
+              if (button == Dialog.BUTTON_OK) {
+                setTimeout(() => {
+                  this.deleteMyAccount(true);
+                }, 500)
+              }
+            }
+          })
+        } else {
+          Dialog.showResultDialog(err.msg);
+        }
+      }, 500);
+    })
+  }
+
+  async requestLogout() {
+    const data = await getStorageJsonSync('loginData');
+    if (data && data.email) {
+      delete data.password
+      setStorageJson('loginData', data);
+      setStorage('RegisterTokenDate', "");
+    }
+    global.userInfo = {}
+    /*this.setState({
+      isLogin: false,
+      userInfo: {}
+    });*/
+    Dialog.dismissLoading();
+    setAccessToken('');
+    startPage(PageList.home);
+  }
+
+  render() {
+    const styles = delStyle();
+    return (
+      <ScrollView
+        style={styles.container}
+        contentContainerStyle={$padding(16)}>
+        <View style={styles.cardView}>
+          <TextView style={styles.textTitle}>We’re sorry to see you go.</TextView>
+          <TextView style={styles.textMessage}>Please be aware that any remaining credits in your account will be forfeited and are non-refundable if you choose to delete your account.</TextView>
+          <TextView style={styles.textMessage}>To proceed with account deletion, please confirm your decision by pressing the "Delete Account" button.</TextView>
+          <TextView style={styles.textMessage}>Once confirmed, your account and all associated data will be permanently deleted and can not be recovered.</TextView>
+          <TextView style={styles.textMessage}>This action is irreversible.</TextView>
+          <View style={styles.confirmView}>
+            <TextView style={styles.textConfirm}>Please Confirm The Following</TextView>
+            <View>
+              <CheckBoxText
+                value={this.state.checkValue1}
+                onValueChange={(newValue) => this.setState({checkValue1: newValue})}
+                flexText={true}
+                text={"I understand that my remaining credits will be forfeited and are non-refundable."}
+                disabled={false}
+                textStyle={{fontSize: 12}}/>
+            </View>
+            <View>
+              <CheckBoxText
+                value={this.state.checkValue2}
+                onValueChange={(newValue) => this.setState({checkValue2: newValue})}
+                flexText={true}
+                text={"I confirm that my account and all associated data will be permanently deleted."}
+                disabled={false}
+                textStyle={{fontSize: 12}}/>
+            </View>
+            <View>
+              <CheckBoxText
+                value={this.state.checkValue3}
+                onValueChange={(newValue) => this.setState({checkValue3: newValue})}
+                flexText={true}
+                text={"I acknowledge that this action is irreversible and can not be undone."}
+                disabled={false}
+                textStyle={{fontSize: 12}}/>
+            </View>
+          </View>
+        </View>
+        <Button
+          style={styles.btnDelete}
+          text="DELETE ACCOUNT"
+          textColor="#FF0F2B"
+          disabled={!(this.state.checkValue1 && this.state.checkValue2 && this.state.checkValue3)}
+          onClick={() => this.deleteAccount()}/>
+      </ScrollView>
+    );
+  }
+}
+
+const delStyle = () => StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: pageBackground
+  },
+  cardView: {
+    padding: 16,
+    borderRadius: 6,
+    backgroundColor: colorLight
+  },
+  btnDelete: {
+    backgroundColor: colorLight,
+    ...$margin(24, 0, 16)
+  },
+  textTitle: {
+    color: textPrimary,
+    fontSize: 16,
+    paddingBottom: 8
+  },
+  textMessage: {
+    color: textSecondary,
+    fontSize: 14,
+    paddingBottom: 16
+  },
+  confirmView: {
+    paddingTop: 16,
+    borderTopWidth: 1,
+    borderColor: "#ededed"
+  },
+  textConfirm: {
+    color: textPrimary,
+    fontSize: 14,
+    fontWeight: "500",
+    paddingBottom: 16
+  }
+})

+ 42 - 21
Strides-APP/app/pages/my/ProfileV2.js

@@ -69,7 +69,7 @@ export default class ProfileV2 extends Component {
       Dialog.dismissLoading();
       //toastShort(err)
       setTimeout(() => {
-        if (err.code == 5334) {
+        if (err.code == 5334 || err.code == 5368) {
           Dialog.showDialog({
             title: $t('profile.deleteAccount'),
             message: err.msg,
@@ -272,7 +272,7 @@ export default class ProfileV2 extends Component {
         <View style={styles.accountList}>
           <CardList refreshId={this.state.refreshId}/>
         </View> */}
-        <Button
+        {/* <Button
           style={styles.cardView}
           viewStyle={styles.profileItem}
           onClick={() => this.deleteAccount()}>
@@ -289,7 +289,7 @@ export default class ProfileV2 extends Component {
             color={textCancel}
             name='angle-right'/>
         </Button>
-        <ShadowViewV2/>
+        <ShadowViewV2/> */}
         <Button
           style={styles.cardView}
           viewStyle={styles.profileItem}
@@ -388,24 +388,45 @@ export default class ProfileV2 extends Component {
           <ShadowViewV2/>
         </>}
         <Button
-            style={styles.cardView}
-            viewStyle={styles.profileItem}
-            onClick={() => startPage(PageList.about)}>
-            <MaterialCommunityIcons
-              style={styles.cardIcon}
-              name="information-outline"
-              color="#00638C"
-              size={32}
-            />
-            <View style={styles.cardInfo}>
-              <TextView style={styles.cardLabel}>{$t('drawer.about')}</TextView>
-            </View>
-            <FontAwesome
-              size={24}
-              color={textCancel}
-              name='angle-right'/>
-          </Button>
-          <ShadowViewV2/>
+          style={styles.cardView}
+          viewStyle={styles.profileItem}
+          onClick={() => startPage(PageList.about)}>
+          <MaterialCommunityIcons
+            style={styles.cardIcon}
+            name="information-outline"
+            color="#00638C"
+            size={32}
+          />
+          <View style={styles.cardInfo}>
+            <TextView style={styles.cardLabel}>{$t('drawer.about')}</TextView>
+          </View>
+          <FontAwesome
+            size={24}
+            color={textCancel}
+            name='angle-right'/>
+        </Button>
+        <ShadowViewV2/>
+        <Button
+          style={styles.cardView}
+          viewStyle={styles.profileItem}
+          onClick={() => {
+            this.deleteAccount();
+            //startPage(PageList.deleteAccount)
+          }}>
+          <MaterialCommunityIcons
+            style={styles.cardIcon}
+            name="account-remove"
+            size={32}
+            color="#00638C"/>
+          <View style={styles.cardInfo}>
+            <TextView style={styles.cardLabel}>{$t('profile.deleteAccount')}</TextView>
+          </View>
+          <FontAwesome
+            size={24}
+            color={textCancel}
+            name='angle-right'/>
+        </Button>
+        <ShadowViewV2/>
         {/* <Button
           style={styles.deleteButton}
           text="DELETE MY ACCOUNT"