Browse Source

perf: 新增订单明细

kuns 1 month ago
parent
commit
f7d1efeec6

+ 1 - 1
.env.preview

@@ -1,2 +1,2 @@
 EXPO_PUBLIC_NODE_ENV=perview
-EXPO_PUBLIC_API_URL=https://charging.ayftech.xyz/api/v1
+EXPO_PUBLIC_API_URL=http://47.115.173.98:12000/api/v1

+ 12 - 0
app/(auth)/(tabs)/(account)/chargingDetailsPage.tsx

@@ -0,0 +1,12 @@
+import { View, Text } from 'react-native';
+import ChargingDetailsPageComponent from '../../../../component/accountPages/chargingDetailsPageComponent';
+
+const ChargingDetailsPage = () => {
+    return (
+        <View className="flex-1">
+            <ChargingDetailsPageComponent />
+        </View>
+    );
+};
+
+export default ChargingDetailsPage;

+ 228 - 0
component/accountPages/chargingDetailsPageComponent.tsx

@@ -0,0 +1,228 @@
+import { View, Text, Pressable, Dimensions,Image, StyleSheet } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+import { router, useLocalSearchParams } from 'expo-router';
+import { CrossLogoSvg } from '../global/SVG';
+import CouponTabViewComponent from '../global/couponTabView';
+import { useEffect, useMemo, useState, useRef } from 'react';
+import { chargeStationService } from '../../service/chargeStationService';
+import BookingTabViewComponent from '../global/bookingTabViewComponent';
+import { set } from 'lodash';
+import { ChargingDetails, Remark, ElectricityPrice, Special } from '../../service/type/chargeStationType';
+import { format, parseISO } from 'date-fns';
+const ChargingDetailsPageComponent = () => {
+    const screenHeight = Dimensions.get('window').height;
+    const params = useLocalSearchParams();
+    const [list, setList] = useState<ChargingDetails>({} as ChargingDetails);
+    const [time, setTime] = useState<string>('');
+  useEffect(() => {
+    const fetchData = async () => {
+      const res = await chargeStationService.fetchChargingDetails(params.id.toString())
+      if (res) {
+        // If res is an array, take the first item; otherwise, set as is
+        setList(Array.isArray(res) ? res[0] : {} as ChargingDetails);
+        // 解析并格式化日期
+        const startTime = parseISO(res[0].actual_start_time);
+        const endTime = parseISO(res[0].actual_end_time);
+        // 格式化为指定格式
+        const formattedDate = `${format(startTime, 'yyyy/MM/dd HH:mm:ss')}-${format(endTime, 'HH:mm:ss')}`;
+        setTime(formattedDate)
+      }
+    };
+    fetchData();
+  }, [])
+  const totalPrice = useMemo(() => {
+    if (list.total_fee && list.withdraw_fee) {
+      let  actual_fee = list.total_fee - list.withdraw_fee
+      const value = actual_fee <= 0? '0': actual_fee % 1 === 0? `${actual_fee}`: `${actual_fee.toFixed(1)}`
+      return value
+    }
+    return '0';
+  }, [list]);
+    return (
+        <SafeAreaView className="flex-1 bg-white" edges={['top']}>
+            <View style={{ minHeight: screenHeight, flex: 1 }}>
+                <View className="mx-[5%]" style={{ marginTop: 25}}>
+                  <Pressable
+                      onPress={() => {
+                          if (router.canGoBack()) {
+                              router.back();
+                          } else {
+                              router.replace('/accountMainPage');
+                          }
+                      }}
+                  >
+                    <CrossLogoSvg />
+                  </Pressable>
+                  <View className="items-center px-3">
+                    {/* <View className="flex-3 items-center  justify-end" style={{}}> */}
+                    <Image
+                        source={require('../../assets/ccLogo.png')}
+                        resizeMode="contain"
+                        style={{
+                            width: screenHeight > 750 ? 200 : 110,
+                            height: screenHeight > 750 ? 200 : 110
+                        }}
+                    />
+                    <Text style={styles.totalPrice}>- {totalPrice}</Text>
+                    <View style={styles.viewLine}></View>
+                    <View className='w-full flex-row justify-between mt-6 pr-10'>
+                      <Text style={styles.leftLable}>訂單编號: </Text>
+                      <Text style={styles.rightLable}>{list.format_order_id}</Text>
+                    </View>
+                    <View className='w-full flex-row justify-between my-3 pr-10'>
+                      <Text style={styles.leftLable}>充電時間: </Text>
+                      <Text style={styles.rightLable}>{time}</Text>
+                    </View>
+                    <View className='w-full flex-row justify-between pr-10 mb-4'>
+                      <Text style={styles.leftLable}>充電站位置:</Text>
+                      <Text style={styles.rightLable}>{params.chargeStationName}</Text>
+                    </View>
+                    <View style={styles.viewLine}></View>
+                    <ChargingDataComponent list={list} totalPrice={totalPrice}/>
+                    <View style={styles.viewLine}></View>
+                    <View className='w-full flex-row justify-between mt-6 pr-10'>
+                      <View>
+                        <Text style={styles.leftLable}>實付:</Text>
+                        {list.promotion_name ? <Text style={{fontSize: 12, color:'#888888'}}>優惠券支付</Text>: null}
+                      </View>
+                      <Text style={styles.rightLable}>${totalPrice}</Text>
+                    </View>
+                  </View>
+                </View>
+                <View style={{ width: "100%",height: 130 }} />
+            </View>
+        </SafeAreaView>
+    );
+};
+interface ChargingDataComponentProps {
+    list: ChargingDetails;
+    totalPrice: string;
+}
+const ChargingDataComponent: React.FC<ChargingDataComponentProps> = ({
+    list,
+    totalPrice
+}) => {
+  const [data, setData] = useState<Remark>({} as Remark);
+  const [price, setPrice] = useState<number>(0);
+   const hasFetchedPrice = useRef(false); // 添加 ref 跟踪是否已获取过价格
+  useEffect(() => {
+    const data = JSON.parse(list?.remark || '{}') as Remark;
+    setData(data)
+    if (!list.promotion_name && !hasFetchedPrice.current && list.actual_start_time) {
+      chargeStationService.fetchElectricityPrice().then(res => {
+        hasFetchedPrice.current = true; // 标记为已调用
+        const date = new Date(list.actual_start_time);
+        const str = date.toLocaleString('en-US', { weekday: 'short' }).toLowerCase();
+        if (res && res.length > 0) { 
+          if (data.RushKwh) {
+            const obj = (res[1][str as keyof ElectricityPrice]) as Special
+            setPrice(obj.price)
+          }
+          if (data.ElseKwh) {
+            const obj = (res[2][str as keyof ElectricityPrice]) as Special
+            setPrice(obj.price)
+          }
+          if (data.OffKwh) {
+            const obj = (res[0][str as keyof ElectricityPrice]) as Special
+            setPrice(obj.price)
+          }
+        }
+      })
+    }
+  }, [list.promotion_name, list.actual_start_time, list.remark])
+  
+  if (!list.promotion_name) {
+    return (
+      <View>
+        {(data.RushKwh) ? 
+        <View>
+          <View className='w-full flex-row justify-between mt-4 pr-10'>
+            <Text style={styles.leftLable}>峰時總電量: </Text>
+            <Text style={styles.rightLable}>{data.RushKwh?.toFixed(1)}</Text>
+          </View>
+          <View className='w-full flex-row justify-between my-3 pr-10'>
+              <Text style={styles.leftLable}>峰時電價(09:00-20:59): </Text>
+            <Text style={styles.rightLable}>${price}</Text>
+          </View>
+          <View className='w-full flex-row justify-between pr-10 mb-3'>
+            <Text style={styles.leftLable}>峰時總電費:</Text>
+            
+            <Text style={styles.rightLable}>${totalPrice}</Text>
+          </View>
+        </View>: null }
+        {(data.ElseKwh) ? 
+        <View>
+          <View className='w-full flex-row justify-between mt-4 pr-10'>
+            <Text style={styles.leftLable}>平時總電量: </Text>
+            <Text style={styles.rightLable}>{data.RushKwh?.toFixed(1)}</Text>
+          </View>
+          <View className='w-full flex-row justify-between my-3 pr-10'>
+            <Text style={styles.leftLable}>平時電價(09:00-20:59): </Text>
+
+            <Text style={styles.rightLable}>${price}</Text>
+          </View>
+          <View className='w-full flex-row justify-between pr-10 mb-3'>
+            <Text style={styles.leftLable}>平時總電費:</Text>
+            
+            <Text style={styles.rightLable}>${totalPrice}</Text>
+          </View>
+        </View>: null }
+        {(data.OffKwh) ? 
+        <View>
+          <View className='w-full flex-row justify-between mt-4 pr-10'>
+            <Text style={styles.leftLable}>穀時總電量: </Text>
+            <Text style={styles.rightLable}>{data.RushKwh?.toFixed(1)}</Text>
+          </View>
+          <View className='w-full flex-row justify-between my-3 pr-10'>
+              <Text style={styles.leftLable}>穀時電價(09:00-20:59): </Text>
+            <Text style={styles.rightLable}>${price}</Text>
+          </View>
+          <View className='w-full flex-row justify-between pr-10 mb-3'>
+            <Text style={styles.leftLable}>穀時總電費:</Text>
+            
+            <Text style={styles.rightLable}>${totalPrice}</Text>
+          </View>
+        </View>: null }
+      </View>
+    )
+  } else {
+    return (
+      <View>
+        <View className='w-full flex-row justify-between mt-4 pr-10'>
+            <Text style={styles.leftLable}>總電量: </Text>
+            <Text style={styles.rightLable}>{data.TotalPower?.toFixed(1)}</Text>
+          </View>
+          <View className='w-full flex-row justify-between my-3 pr-10'>
+              <Text style={styles.leftLable}>電價: </Text>
+            <Text style={styles.rightLable}>${list?.connector?.EquipmentID?.StationID?.price}</Text>
+          </View>
+          <View className='w-full flex-row justify-between pr-10 mb-3'>
+            <Text style={styles.leftLable}>總電費: </Text>
+            
+            <Text style={styles.rightLable}>${totalPrice}</Text>
+          </View>
+      </View>
+    )
+  }
+
+}
+const styles = StyleSheet.create({
+  viewLine: {
+    width: '100%',
+    height: 1,
+    backgroundColor: '#E5E5E5',
+  },
+  totalPrice: {
+    fontSize: 26,
+    fontWeight: 'bold',
+    marginBottom: 25,
+  },
+  leftLable: {
+    fontSize: 18,
+    color:'#888888',
+  },
+  rightLable: {
+    fontSize: 17
+  },
+})
+export default ChargingDetailsPageComponent;

+ 3 - 1
component/global/bookingTabViewComponent.tsx

@@ -66,6 +66,7 @@ const processReservations = (reservations: any [], allStations: string [], isFut
         })
         .slice(0, 33)
         .map((reservation) => {
+
             // 添加对 reservation 的安全检查
             if (!reservation) {
                 return {} as TabItem; // 返回默认对象
@@ -112,7 +113,8 @@ const processReservations = (reservations: any [], allStations: string [], isFut
                 withdraw_fee: reservation.withdraw_fee || 0,
                 actual_fee: (reservation.total_fee || 0) - (reservation.withdraw_fee || 0),
                 current_price: snapshot_price?.current_price || 0,
-                total_power: reservation.total_power || 0
+                total_power: reservation.total_power || 0,
+                id: reservation.id || ''
             } as TabItem;
         });
 };

+ 15 - 11
component/global/chargingRecord.tsx

@@ -33,6 +33,7 @@ export interface TabItem {
     actual_end_time?: string;
     actual_fee?: number;
     current_price?: number;
+    id?: string;
 }
 
 interface TabViewComponentProps {
@@ -157,17 +158,6 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                                 : ''}
                         </Text>
                     </View>
-                    <View className="flex flex-row  space-x-2">
-                        <Text
-                            style={{
-                                fontWeight: '400',
-                                fontSize: 14,
-                                color: '#222222'
-                            }}
-                        >
-                            每度電金額: ${item.current_price}
-                        </Text>
-                    </View>
                     <Text
                         style={{
                             fontWeight: '400',
@@ -177,6 +167,20 @@ const TabItem = ({ item, currentLocation }: { item: TabItem; currentLocation: Lo
                     >
                         {item.chargeStationName}
                     </Text>
+                    <View className="flex flex-row space-x-2">
+                        <Text
+                            style={{
+                                fontWeight: '400',
+                                fontSize: 14,
+                                color: '#02677D'
+                            }}
+                            onPress={() => {
+                                router.push({ pathname: 'chargingDetailsPage', params: { id: item.id, chargeStationName: item.chargeStationName }})
+                            }}
+                        >
+                            訂單詳情 &gt;
+                        </Text>
+                    </View>
                 </View>
             </View>
         </Pressable>

+ 0 - 1
component/registrationMultiStepForm/formComponent/formPages/loginPage.tsx

@@ -97,7 +97,6 @@ const LoginPage: React.FC<LoginPageProps> = ({ goToNextPage, goToForgetPassWordP
         router.push('(public)/userTermsPage');
     };
     const insets = useSafeAreaInsets();
-    console.log(screenHeight);
     return (
         <ScrollView
             contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}

+ 26 - 1
service/chargeStationService.tsx

@@ -2,7 +2,7 @@ import axios, { isAxiosError } from 'axios';
 import { Alert } from 'react-native';
 import * as SecureStore from 'expo-secure-store';
 import {apiClient} from './requets'
-
+import { ChargingDetails, ElectricityPrice } from './type/chargeStationType';
 class ChargeStationService {
 
     async fetchCarBrand() {
@@ -574,5 +574,30 @@ class ChargeStationService {
             return false;
         }
     }
+    async fetchChargingDetails(recordID: string) {
+        try {
+            const response = await apiClient.instance.get(`/clients/reservation/getRecordById/${recordID}`);
+            if (response.status === 200 || response.status === 201) {
+                return response.data as ChargingDetails [];
+            } else {
+                return []
+            }
+        } catch (error) {
+            console.log(error);
+        }
+    }
+    async fetchElectricityPrice() {
+        try {
+            const response = await apiClient.instance.get(`/clients/promotion/business_hourGroup`);
+            if (response.status === 200 || response.status === 201) {
+                return response.data as ElectricityPrice [];
+            } else {
+                return [];
+            }
+        } catch (error) {
+            console.log(error);
+        }
+
+    }
 }
 export const chargeStationService = new ChargeStationService();

+ 218 - 0
service/type/chargeStationType.ts

@@ -0,0 +1,218 @@
+export interface StationID {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	snapshot: string;
+	self_active_status_fk: string;
+	business_hours_fk: string;
+	qr_code: string;
+	image: string;
+	price: number;
+	pricemodel_id: string;
+}
+
+export interface EquipmentID {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	equipmentID: string;
+	equipmentLat: number;
+	equipmentLng: number;
+	equipmentType: number;
+	equipmentName: string;
+	equipmentModel: string;
+	power: number;
+	StationID: StationID;
+}
+
+export interface Connector {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	connectorID: string;
+	voltageLowerLimits: number;
+	connectorType: number;
+	voltageUpperLimits: number;
+	nationalStandard: number;
+	connectorName: string;
+	current: number;
+	park: number;
+	power: number;
+	status: string;
+	Parkstatus: string;
+	Lockstatus: string;
+	EquipmentID: EquipmentID;
+}
+
+export interface User {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	firstname?: any;
+	lastname?: any;
+	nickname: string;
+	email: string;
+	password: string;
+	phone: string;
+	ic_card: string;
+	wallet: number;
+	icon_url?: any;
+	remark?: any;
+	address?: any;
+	status_fk: string;
+	gender: string;
+	birthday: string;
+	ic_car_id?: any;
+	notify_session_id: string;
+}
+
+export interface Statu {
+	id: string;
+	createdAt: string;
+	updatedAt: string;
+	description: string;
+}
+
+export interface Car_brand {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	name: string;
+	img_url: string;
+}
+
+export interface Car_type {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	name: string;
+	capacitance: number;
+	capacitance_unit: string;
+	type_image_url?: any;
+}
+
+export interface Car {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	license_plate: string;
+	car_brand: Car_brand;
+	car_type: Car_type;
+}
+
+export interface ChargingDetails {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	book_time: string;
+	end_time: string;
+	actual_start_time: string;
+	actual_end_time: string;
+	actual_total_power: number;
+	total_power: number;
+	type: string;
+	penalty_fee: number;
+	total_fee: number;
+	snapshot: string;
+	remark: string;
+	promotion_name?: any;
+	promotion_msg?: any;
+	format_order_id: string;
+	soc: number;
+	paid_status: boolean;
+	penalty_paid_status: boolean;
+	withdraw_fee: number;
+	connector: Connector;
+	user: User;
+	status: Statu;
+	car: Car;
+}
+export interface Remark {
+  EndTime: string;
+  OffService: number;
+  PeakCharge: number;
+  StartTime: string;
+  PeakKwh: number;
+  ElseService: number;
+  OffCharge: number;
+  RushKwh: number;
+  ElseCharge: number;
+  PeakService: number;
+  TotalPower: number;
+  StartChargeSeq: string;
+  RushCharge: number;
+  TotalElectMoney: number;
+  OffKwh: number;
+  TotalServiceMoney: number;
+  PriceModelID: number;
+  TotalMoney: number;
+  RushService: number;
+  StopReason: number;
+  ConnectorID: string;
+  ElseKwh: number;
+}
+export interface Mon {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Tue {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Wed {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Thu {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Fri {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Sat {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Sun {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface Special {
+	from: string;
+	to: string;
+	price: number;
+}
+
+export interface ElectricityPrice {
+	createdAt: string;
+	updatedAt: string;
+	id: string;
+	event_name: string;
+	mon: Mon;
+	tue: Tue;
+	wed: Wed;
+	thu: Thu;
+	fri: Fri;
+	sat: Sat;
+	sun: Sun;
+	special: Special;
+	is_active: boolean;
+	expire_date: string;
+	pricemodel_id: string;
+}