feat: 优化订单状态显示、地图标记和用户交互体验
- 在订单状态9和10之间新增“待评价”状态显示
- 优化订单详情页的地图展示和样式布局
- 改进取消订单弹窗,根据行程状态动态显示到达时间
- 修复车辆标记在特定状态下的显示问题
- 调整解锁操作的提示文案,提升用户体验
- 增强WebSocket消息日志记录,排除pong类型消息
- 优化价格明细页面的条件渲染逻辑
- 修复城市切换后地图中心点定位问题
| | |
| | | const skipNextLocationCenterRef = useRef(false); |
| | | const skipCityChangeEffectRef = useRef(false); |
| | | const isCitySwitchingRef = useRef(false); |
| | | const shouldMoveToSwitchedCityCenterRef = useRef(false); |
| | | const lastSelectedCityCodeRef = useRef<string | undefined>(Taro.getStorageSync('selectedCity')?.code); |
| | | const orderStatusBound = useRef(false); |
| | | |
| | |
| | | |
| | | const mapActiveStatusToTripStatus = (status: number) => { |
| | | if (status === 0) return 1; |
| | | if (status === 1) return 2; |
| | | if (status === 1) return 3; |
| | | if (status === 3 || status === 2) return 4; |
| | | if (status === 4) return 5; |
| | | if (status === 5) return 6; |
| | |
| | | const nextCode = selectedCity.code; |
| | | if (lastSelectedCityCodeRef.current && nextCode && lastSelectedCityCodeRef.current !== nextCode) { |
| | | isCitySwitchingRef.current = true; |
| | | shouldMoveToSwitchedCityCenterRef.current = true; |
| | | Taro.removeStorageSync('startSite'); |
| | | Taro.removeStorageSync('endSite'); |
| | | setCurrentSite({}); |
| | |
| | | if (startSite && startSite.latitude && startSite.longitude) { |
| | | if (isCitySwitchingRef.current) { |
| | | isCitySwitchingRef.current = false; |
| | | return; |
| | | const shouldMoveToCityCenter = shouldMoveToSwitchedCityCenterRef.current; |
| | | shouldMoveToSwitchedCityCenterRef.current = false; |
| | | if (shouldMoveToCityCenter) { |
| | | return; |
| | | } |
| | | } |
| | | setLatitude(startSite.latitude); |
| | | setLongitude(startSite.longitude); |
| | |
| | | } else { |
| | | if (isCitySwitchingRef.current) { |
| | | isCitySwitchingRef.current = false; |
| | | skipNextLocationCenterRef.current = true; |
| | | const shouldMoveToCityCenter = shouldMoveToSwitchedCityCenterRef.current; |
| | | shouldMoveToSwitchedCityCenterRef.current = false; |
| | | if (!shouldMoveToCityCenter) { |
| | | const shouldUpdateCenter = !skipNextLocationCenterRef.current; |
| | | skipNextLocationCenterRef.current = false; |
| | | const hasEntered = Taro.getStorageSync(FIRST_ENTER_KEY) === 'true'; |
| | | if (!hasEntered) { |
| | | skipFenceIncludePointsRef.current = true; |
| | | getLocationInfo(true); |
| | | Taro.setStorageSync(FIRST_ENTER_KEY, 'true'); |
| | | return; |
| | | } |
| | | getLocationInfo(shouldUpdateCenter); |
| | | return; |
| | | } |
| | | const switchedCity = Taro.getStorageSync('selectedCity'); |
| | | if (switchedCity?.name) { |
| | | getCityLocation(switchedCity.name).then((loc) => { |
| | | setLatitude(loc.latitude); |
| | | setLongitude(loc.longitude); |
| | | shouldIgnoreRegionChange.current = true; |
| | | skipNextLocationCenterRef.current = true; |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | mapCtx.moveToLocation({ |
| | | latitude: loc.latitude, |
| | | longitude: loc.longitude, |
| | | }); |
| | | if (switchedCity?.code) { |
| | | updateMapData(loc.latitude, loc.longitude, switchedCity.code); |
| | | } |
| | | }).catch(() => { |
| | | skipNextLocationCenterRef.current = true; |
| | | }); |
| | | } else { |
| | | skipNextLocationCenterRef.current = true; |
| | | } |
| | | return; |
| | | } |
| | | const shouldUpdateCenter = !skipNextLocationCenterRef.current; |
| | |
| | | const toOrigin = async () => { |
| | | const ok = await ensureNetwork(); |
| | | if (!ok) return; |
| | | const systemSetting = await Promise.resolve((Taro as any).getSystemSetting?.()); |
| | | if (systemSetting?.locationEnabled === false) { |
| | | Taro.showToast({ |
| | | title: '需开启设备GPS/定位服务开关', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (isLocationAuthDenied) return; |
| | | shouldIgnoreRegionChange.current = false; |
| | | isReturningToOrigin.current = true; |
| | |
| | | </View> |
| | | {/* 订单状态卡片 */} |
| | | { |
| | | [2, 3, 4, 5, 6, 7, 8].includes(tripStatus) && |
| | | [3, 4, 5, 6, 7, 8].includes(tripStatus) && |
| | | <View className="order-status"> |
| | | { |
| | | [2].includes(tripStatus) && |
| | | [3].includes(tripStatus) && |
| | | <View className="order-status-left"> |
| | | <View className="order-status-left-item"> |
| | | <Image className="order-status-left-item-icon" src={require('../../../robotaxi/assets/pointer.png')} /> |
| | |
| | | </View> |
| | | } |
| | | { |
| | | [3, 4, 5, 6, 7, 8].includes(tripStatus) && |
| | | [4, 5, 6, 7, 8].includes(tripStatus) && |
| | | <View className="order-status-left"> |
| | | { |
| | | orderData.vehiclePictures ? |
| | |
| | | const isFailure = res?.success === false || (typeof res?.code === 'number' && res.code !== 0); |
| | | if (isFailure) { |
| | | Taro.showToast({ |
| | | title: res?.msg || '网络连接不稳定,解锁失败,请重试', |
| | | title: res?.msg || '解锁失败,请重试或联系客服', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | if (doorType === 'trunk') { |
| | | Taro.showToast({ |
| | | title: '后备箱已解锁', |
| | | title: '打开成功', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | if (doorType === 'door') { |
| | | Taro.showToast({ |
| | | title: '车门已解锁', |
| | | title: '右侧车门已解锁', |
| | | icon: 'none' |
| | | }); |
| | | if (!hasShownAnnouncementsRef.current) { |
| | |
| | | } |
| | | }).catch(() => { |
| | | Taro.showToast({ |
| | | title: res?.msg || '网络连接不稳定,解锁失败,请重试', |
| | | title: res?.msg || '解锁失败,请重试或联系客服', |
| | | icon: 'none' |
| | | }); |
| | | }); |
| | |
| | | import { useEffect, useState } from 'react'; |
| | | import './css-folder/cancel-popup.less' |
| | | import { |
| | | clearOrderTripStorage |
| | | clearOrderTripStorage, |
| | | formatDurationDisplay |
| | | } from '../../../../utils/Robotaxi-public-method' |
| | | |
| | | |
| | |
| | | startSite?: any; |
| | | endSite?: any; |
| | | waitMinutes?: number; |
| | | tripStatus?: number; |
| | | routeDurationSeconds?: number | null; |
| | | discountAmount?: number; |
| | | onClose?: () => void; |
| | | onConfirm?: () => void; |
| | | } |
| | | |
| | | export default function CancelPopup({ show = false, cancelType = 1, startSite, endSite, waitMinutes, discountAmount, onClose, onConfirm }: Props) { |
| | | export default function CancelPopup({ show = false, cancelType = 1, startSite, endSite, waitMinutes, tripStatus, routeDurationSeconds, discountAmount, onClose, onConfirm }: Props) { |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const cachedStartSite = startSite || Taro.getStorageSync('startSite') || {}; |
| | |
| | | const startSiteName = cachedStartSite?.name || cachedStartSite?.address || '-'; |
| | | const endSiteName = cachedEndSite?.name || cachedEndSite?.address || '-'; |
| | | const waitMinutesValue = typeof waitMinutes === 'number' && !Number.isNaN(waitMinutes) ? waitMinutes : 2; |
| | | const durationDisplay = formatDurationDisplay(routeDurationSeconds); |
| | | const discountAmountValue = typeof discountAmount === 'number' && !Number.isNaN(discountAmount) ? discountAmount : 0; |
| | | |
| | | const STATUS_MAP = { |
| | |
| | | tip: `本单限时优惠${discountAmountValue}元,重新叫车可能等待更久`, |
| | | }, |
| | | 2: { |
| | | title: `车辆还有${waitMinutesValue}分钟到达,确定取消吗`, |
| | | title: tripStatus === 4 |
| | | ? `车辆还有${durationDisplay.value}${durationDisplay.unit}到达,确定取消吗` |
| | | : `车辆还有${waitMinutesValue}分钟到达,确定取消吗`, |
| | | tip: `本单限时优惠${discountAmountValue}元,重新叫车可能等待更久`, |
| | | }, |
| | | 3: { |
| | |
| | | }, [show]); |
| | | |
| | | const closePopup = () => { |
| | | if (cancelType == 5) { |
| | | onConfirm?.(); |
| | | onClose?.(); |
| | | return |
| | | } |
| | | if (cancelType == 4) { |
| | | Taro.reLaunch({ |
| | | url: '/pages/robotaxi/home/index', |
| | |
| | | export default function PassengerNotice({ show = false, onClose }: Props) { |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const [checked, setChecked] = useState(false); |
| | | const [checked, setChecked] = useState(true); |
| | | const [noticeContent, setNoticeContent] = useState(''); |
| | | |
| | | useEffect(() => { |
| | |
| | | const [polyline, setPolyline] = useState<any[]>([]); // 路线 |
| | | const [startSite, setStartSite] = useState<any>(null); // 上车点 |
| | | const [endSite, setEndSite] = useState<any>({}); // 下车点 |
| | | const [tripStatus, setTripStatus] = useState<number>(Number(Taro.getStorageSync('tripStatus')) || 1); // 行程状态 1:发布行程 2:发布中 3:呼叫车中 4:车辆即将到达 5:车辆到达上车点 6:车辆行驶中 7:到达目的地 8:行程已结束 9:支付成功 |
| | | const [tripStatus, setTripStatus] = useState<number>(Number(Taro.getStorageSync('tripStatus')) || 1); // 行程状态 1:发布行程 2:发布中 3:呼叫车中 4:车辆即将到达 5:车辆到达上车点 6:车辆行驶中 7:到达目的地 8:行程已结束 9:支付成功 10:取消订单时页面只展示一个上车点 |
| | | const [elapsedSeconds, setElapsedSeconds] = useState(0); // 等待计时秒数 |
| | | const requoteTriggeredRef = useRef(false); |
| | | const [showCancelPopup, setShowCancelPopup] = useState(false); // 取消弹窗是否显示 |
| | |
| | | 7: { showPickup: false, showDropoff: true, showCar: false, showRoute: false, showWalking: false, viewport: 'dropoff', translateCar: false }, |
| | | 8: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: false, viewport: 'order_detail', translateCar: false }, |
| | | 9: { showPickup: true, showDropoff: true, showCar: false, showRoute: true, showWalking: false, viewport: 'order_detail', translateCar: false }, |
| | | 10: { showPickup: true, showDropoff: false, showCar: false, showRoute: false, showWalking: false, viewport: 'pickup', translateCar: false }, |
| | | }; |
| | | const statusRule = useMemo(() => STATUS_RULES[tripStatus] || STATUS_RULES[1], [tripStatus]); |
| | | const shouldShowRoute = statusRule.showRoute; |
| | |
| | | const lastRoutePointRef = useRef<{ latitude: number; longitude: number } | null>(null); |
| | | const lastRouteUpdateAtRef = useRef<number>(0); |
| | | const lastPolylineUpdateAtRef = useRef<number>(0); |
| | | const lastRouteAndFareRequestAtRef = useRef<number>(0); |
| | | const markersSignatureRef = useRef<string>(''); |
| | | const isAndroid = useMemo(() => { |
| | | try { |
| | | const info = Taro.getSystemInfoSync(); |
| | |
| | | } |
| | | }, []); |
| | | const shouldShowWaitingRadar = isAndroid && [2, 3].includes(tripStatus); |
| | | const vehicleMarkerId = !isAndroid && tripStatus === 6 ? 30 : 3; |
| | | const isCheckedEnter = useRef(false); |
| | | const router = Taro.useRouter(); |
| | | |
| | | useEffect(() => { |
| | | const wasHold = [6].includes(lastTripStatusRef.current); |
| | | const isHold = [6].includes(tripStatus); |
| | | const wasHold = [4, 6].includes(lastTripStatusRef.current); |
| | | const isHold = [4, 6].includes(tripStatus); |
| | | if (!wasHold && isHold) { |
| | | orderRouteInitializedRef.current = false; |
| | | tripStatus6ViewportLockedRef.current = false; |
| | |
| | | }; |
| | | |
| | | const updateCarCalloutPosition = () => { |
| | | if (!isAndroid) { |
| | | return; |
| | | } |
| | | if (!vehicleLocation || ![4, 6].includes(tripStatus)) { |
| | | if (!isAndroid || !vehicleLocation || ![4, 6].includes(tripStatus)) { |
| | | setCarCalloutVisible(false); |
| | | return; |
| | | } |
| | |
| | | |
| | | // 请求路线和车费 |
| | | const requestRouteAndFare = async (originLat: number, originLng: number, destLat: number, destLng: number, force = false) => { |
| | | const now = Date.now(); |
| | | if (now - lastRouteAndFareRequestAtRef.current < 1000) { |
| | | return; |
| | | } |
| | | lastRouteAndFareRequestAtRef.current = now; |
| | | const requestKey = `${originLng},${originLat}-${destLng},${destLat}`; |
| | | if (!force && requestKey === routeRequestKeyRef.current) { |
| | | return; |
| | |
| | | lastRouteUpdateAtRef.current = 0; |
| | | } |
| | | if (payload?.showCancelPopup) { |
| | | handleOpenCancelPopupWithType(5); |
| | | setTripStatus(10); |
| | | setCancelType(5); |
| | | setShowCancelPopup(true); |
| | | } |
| | | if (payload?.showPassengerNotice) { |
| | | checkAndShowPassengerNotice(); |
| | |
| | | useEffect(() => { |
| | | const pickupSite = startSite?.latitude && startSite?.longitude ? startSite : Taro.getStorageSync('startSite'); |
| | | const dropoffSite = endSite?.latitude && endSite?.longitude ? endSite : Taro.getStorageSync('endSite'); |
| | | const useAndroidNativeTrip1Callout = isAndroid && tripStatus === 1; |
| | | const pickupMarker = pickupSite?.latitude && pickupSite?.longitude ? { |
| | | id: 1, |
| | | latitude: pickupSite.latitude, |
| | |
| | | anchor: { x: 0.5, y: 1 }, |
| | | zIndex: 1, |
| | | name: pickupSite?.name || '上车点', |
| | | customCallout: { |
| | | anchorY: [2, 3].includes(tripStatus) ? 150 : -5, |
| | | anchorX: 0, |
| | | display: 'ALWAYS' |
| | | } |
| | | ...(useAndroidNativeTrip1Callout |
| | | ? { |
| | | callout: { |
| | | content: `${pickupSite?.name || '上车点'} >`, |
| | | color: '#000000', |
| | | fontSize: 12, |
| | | bgColor: '#FFFFFF', |
| | | borderRadius: 8, |
| | | padding: 8, |
| | | display: 'ALWAYS', |
| | | textAlign: 'center', |
| | | }, |
| | | } |
| | | : { |
| | | customCallout: { |
| | | anchorY: [2, 3].includes(tripStatus) ? 150 : -5, |
| | | anchorX: 0, |
| | | display: 'ALWAYS' |
| | | }, |
| | | }) |
| | | } : null; |
| | | const dropoffMarker = dropoffSite?.latitude && dropoffSite?.longitude ? { |
| | | id: 2, |
| | |
| | | anchor: { x: 0.5, y: 1 }, |
| | | zIndex: 1, |
| | | name: [7, 9].includes(tripStatus) ? '已到达' : (dropoffSite.name || '目的地'), |
| | | customCallout: { |
| | | anchorY: -5, |
| | | anchorX: 0, |
| | | display: 'ALWAYS' |
| | | } |
| | | ...(useAndroidNativeTrip1Callout |
| | | ? { |
| | | callout: { |
| | | content: `${[7, 9].includes(tripStatus) ? '已到达' : (dropoffSite.name || '目的地')} >`, |
| | | color: '#000000', |
| | | fontSize: 12, |
| | | bgColor: '#FFFFFF', |
| | | borderRadius: 8, |
| | | padding: 8, |
| | | display: 'ALWAYS', |
| | | textAlign: 'center', |
| | | }, |
| | | } |
| | | : { |
| | | customCallout: { |
| | | anchorY: -5, |
| | | anchorX: 0, |
| | | display: 'ALWAYS' |
| | | }, |
| | | }) |
| | | } : null; |
| | | const carMarker = vehicleLocation ? { |
| | | id: 3, |
| | | id: vehicleMarkerId, |
| | | latitude: vehicleLocation.latitude, |
| | | longitude: vehicleLocation.longitude, |
| | | iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e51e92e6-700d-4e83-b3e0-14e6f18e0b9a.png', |
| | |
| | | zIndex: 1, |
| | | rotate: typeof vehicleDirection === 'number' ? vehicleDirection : 0, |
| | | name: '车辆位置', |
| | | customCallout: { anchorY: 50, anchorX: 0, display: 'ALWAYS' } |
| | | customCallout: { anchorY: 70, anchorX: 0, display: 'ALWAYS' } |
| | | } : null; |
| | | |
| | | const nextMarkers: any[] = []; |
| | | if (statusRule.showPickup && pickupMarker) nextMarkers.push(pickupMarker); |
| | | if (statusRule.showCar && carMarker) nextMarkers.push(carMarker); |
| | | if (statusRule.showDropoff && dropoffMarker) nextMarkers.push(dropoffMarker); |
| | | const nextMarkersSignature = nextMarkers |
| | | .map((m: any) => [ |
| | | m.id, |
| | | Number(m.latitude).toFixed(6), |
| | | Number(m.longitude).toFixed(6), |
| | | m.name || '', |
| | | typeof m.rotate === 'number' ? m.rotate : '', |
| | | m?.customCallout?.anchorY ?? '', |
| | | ].join('|')) |
| | | .join(';'); |
| | | const hasDropoffMarker = markersRef.current.some((m: any) => m.id === 2); |
| | | const shouldHoldMarkerByTranslate = tripStatus === 6 && carMarkerInitialized.current && statusRule.showCar && (!statusRule.showDropoff || hasDropoffMarker); |
| | | const shouldHoldMarkerByTranslate = [4, 6].includes(tripStatus) && carMarkerInitialized.current && statusRule.showCar && (!statusRule.showDropoff || hasDropoffMarker); |
| | | const shouldSkipUpdate = shouldHoldMarkerByTranslate && (isAndroid || tripStatus6MarkerRefreshedRef.current); |
| | | if (shouldSkipUpdate) { |
| | | } else { |
| | | setMarkers(nextMarkers); |
| | | if (!isAndroid && tripStatus === 6 && statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current) { |
| | | if (markersSignatureRef.current !== nextMarkersSignature) { |
| | | markersSignatureRef.current = nextMarkersSignature; |
| | | setMarkers(nextMarkers); |
| | | } |
| | | if (!isAndroid && [4, 6].includes(tripStatus) && statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current) { |
| | | tripStatus6MarkerRefreshedRef.current = true; |
| | | } |
| | | } |
| | | carMarkerInitialized.current = statusRule.showCar && !!carMarker; |
| | | }, [tripStatus, startSite?.name, startSite?.latitude, startSite?.longitude, endSite.name, endSite?.latitude, endSite?.longitude, vehicleLocation, vehicleDirection]); |
| | | }, [tripStatus, startSite?.name, startSite?.latitude, startSite?.longitude, endSite.name, endSite?.latitude, endSite?.longitude, vehicleLocation, vehicleDirection, vehicleMarkerId]); |
| | | |
| | | // 绘制路线与视野适配 |
| | | useEffect(() => { |
| | |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | Taro.nextTick(() => { |
| | | mapCtx.translateMarker({ |
| | | markerId: 3, |
| | | markerId: vehicleMarkerId, |
| | | destination: { |
| | | latitude: vehicleLocation.latitude, |
| | | longitude: vehicleLocation.longitude, |
| | |
| | | }, |
| | | }); |
| | | }); |
| | | }, [tripStatus, vehicleLocation, vehicleDirection]); |
| | | }, [tripStatus, vehicleLocation, vehicleDirection, vehicleMarkerId]); |
| | | |
| | | const bindSocketListeners = () => { |
| | | if (!orderStatusBound.current && orderStatusHandlerRef.current) { |
| | |
| | | |
| | | // 页面初始化 |
| | | useDidShow(() => { |
| | | const { skipPageFlag } = router.params; |
| | | if (skipPageFlag && Taro.getStorageSync('orderId')) { |
| | | Taro.showToast({ |
| | | title: '您已有进行中的订单', |
| | | icon: 'none', |
| | | }) |
| | | } |
| | | const s = Taro.getStorageSync('startSite'); |
| | | const e = Taro.getStorageSync('endSite'); |
| | | const cachedOrderId = Taro.getStorageSync('orderId'); |
| | |
| | | |
| | | // 确认取消 |
| | | const handleConfirmCancel = async () => { |
| | | if (cancelType === 5) { |
| | | handleOpenCancelPopupWithType(5); |
| | | return; |
| | | } |
| | | const targetOrderId = orderId || Taro.getStorageSync('orderId'); |
| | | if (targetOrderId) { |
| | | try { |
| | |
| | | Taro.removeStorageSync('pickupEndTime'); |
| | | Taro.removeStorageSync('waitingTimerStartAt'); |
| | | Taro.removeStorageSync('waitingTimerElapsed'); |
| | | setTripStatus(10); |
| | | setCancelType(4); |
| | | return |
| | | } catch (error) { |
| | |
| | | > |
| | | {/* 自定义callout */} |
| | | { |
| | | markers.map((item, index) => { |
| | | !(isAndroid && tripStatus === 1) && markers.map((item, index) => { |
| | | const calloutKey = `${tripStatus}-${item.id}`; |
| | | const isWaiting = [2, 3].includes(tripStatus) && index === 0; |
| | | |
| | | if (isWaiting) { |
| | | return ( |
| | | <CoverView slot="callout" marker-id={item.id} key={`${tripStatus}-${item.id}`} className="waiting-callout"> |
| | | <CoverView slot="callout" marker-id={item.id} key={calloutKey} className="waiting-callout"> |
| | | <CoverView className="waiting-callout-content"> |
| | | {/* <CoverView className="waiting-callout-bg"> |
| | | <CoverImage className="waiting-callout-bg-img" src={waitingCalloutBgSrc} /> |
| | |
| | | return null; |
| | | } |
| | | return ( |
| | | <CoverView slot="callout" marker-id={item.id} key={`${tripStatus}-${item.id}`} className="custom-callout"> |
| | | <CoverView slot="callout" marker-id={item.id} key={calloutKey} className="custom-callout"> |
| | | { |
| | | (item.name !== '车辆位置' && !(item.id === 2 && [7, 9].includes(tripStatus))) && |
| | | <CoverView className={`custom-callout-content pr-6 ${isAndroid && 'txt-center '}`}> |
| | | <CoverView className={`custom-callout-content ${isAndroid && 'txt-center '}`}> |
| | | <CoverView>{item.name}</CoverView> |
| | | { |
| | | isAndroid && |
| | |
| | | </CoverView> |
| | | } |
| | | { |
| | | (item.name === '车辆位置' && [4, 6].includes(tripStatus)) && |
| | | <CoverView className="custom-callout-content" style={{ padding: '6px 10px' }}> |
| | | (item.name === '车辆位置' && [4, 6].includes(tripStatus) && !isAndroid) && |
| | | <CoverView className="custom-callout-content" style={{ padding: '6px 10px', minWidth: '132px' }}> |
| | | <CoverView className="car-time"> |
| | | <CoverView className="car-time-num">{routeDistance?.value ?? 0}</CoverView> |
| | | <CoverView>{routeDistance?.unit === 'm' ? '米' : '公里'}</CoverView> |
| | |
| | | } |
| | | {/* 呼叫车辆弹窗 */} |
| | | { |
| | | tripStatus >= 3 && |
| | | tripStatus >= 3 && tripStatus !== 10 && |
| | | <CallingCar |
| | | key={tripStatus} |
| | | orderId={orderId} |
| | |
| | | startSite={startSite} |
| | | endSite={endSite} |
| | | waitMinutes={fareEstimate?.waitingTime} |
| | | tripStatus={tripStatus} |
| | | routeDurationSeconds={routeDuration} |
| | | discountAmount={fareEstimate?.discountAmount} |
| | | onClose={handleCloseCancelPopup} |
| | | onConfirm={handleConfirmCancel} |
| | |
| | | <View className='price-notice-label'>{pricingCard.time}({formatTripDistance(pricingCard.distance)},{formatTripDurationFromSeconds(pricingCard.duration)})</View> |
| | | <View className='price-notice-value'>{pricingCard.startPriceStr}元</View> |
| | | </View> |
| | | <View className='flex-between-card'> |
| | | <View className='price-label'>里程费</View> |
| | | <View className='price-value'>{pricingCard.distancePriceStr}元</View> |
| | | </View> |
| | | <View className='flex-between-card mb-16'> |
| | | <View className='price-notice-label'>普通时段({formatTripDistance(pricingCard.distancePriceNormal)})</View> |
| | | <View className='price-notice-value'>{pricingCard.distancePriceStr}元</View> |
| | | </View> |
| | | <View className='flex-between-card'> |
| | | <View className='price-label'>时长费({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View> |
| | | <View className='price-value'>{pricingCard.durationPriceStr}元</View> |
| | | </View> |
| | | <View className='flex-between-card mb-16'> |
| | | <View className='price-notice-label'>普通时段({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View> |
| | | <View className='price-notice-value'>{pricingCard.durationPriceStr}元</View> |
| | | </View> |
| | | { |
| | | pricingCard.distancePriceStr > 0 && |
| | | <View> |
| | | <View className='flex-between-card'> |
| | | <View className='price-label'>里程费</View> |
| | | <View className='price-value'>{pricingCard.distancePriceStr}元</View> |
| | | </View> |
| | | <View className='flex-between-card mb-16'> |
| | | <View className='price-notice-label'>普通时段({formatTripDistance(pricingCard.distancePriceNormal)})</View> |
| | | <View className='price-notice-value'>{pricingCard.distancePriceStr}元</View> |
| | | </View> |
| | | </View> |
| | | } |
| | | { |
| | | pricingCard.durationPriceStr > 0 && |
| | | <View> |
| | | <View className='flex-between-card'> |
| | | <View className='price-label'>时长费({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View> |
| | | <View className='price-value'>{pricingCard.durationPriceStr}元</View> |
| | | </View> |
| | | <View className='flex-between-card mb-16'> |
| | | <View className='price-notice-label'>普通时段({formatTripDurationFromSeconds(pricingCard.durationPriceNormal)})</View> |
| | | <View className='price-notice-value'>{pricingCard.durationPriceStr}元</View> |
| | | </View> |
| | | </View> |
| | | } |
| | | {pricingCard.coupon && ( |
| | | <View className='flex-between-card'> |
| | | <View className='price-label'>优惠券</View> |
| | |
| | | {/* 总价格 */} |
| | | <View className='flex-card total'> |
| | | <Text className='total-text'>总费用</Text> |
| | | <Text className='num'>{pricingCard.totalPriceStr}</Text> |
| | | <Text className='num'>{pricingCard.finalFareStr}</Text> |
| | | <Text className='unit'>元</Text> |
| | | </View> |
| | | {/* 质疑事项 */} |
| | |
| | | .order-detail-page { |
| | | background: #F2F3F7; |
| | | min-height: 100vh; |
| | | padding: 32px 0px; |
| | | padding: 32px 24px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .car-info { |
| | | margin: 0 24px; |
| | | padding: 18px 32px 32px 32px; |
| | | border-radius: 24px; |
| | | background: #FFFFFF; |
| | |
| | | .info-top { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 36px; |
| | | |
| | | .car-name { |
| | | font-family: PingFang SC; |
| | |
| | | } |
| | | |
| | | .evaluate-card { |
| | | margin-top: 36px; |
| | | width: 638px; |
| | | height: 248px; |
| | | border-radius: 16px; |
| | |
| | | } |
| | | |
| | | .trip-info { |
| | | margin: 24px 38px; |
| | | margin-top: 24px; |
| | | border-radius: 20.29px; |
| | | background: #FFFFFF; |
| | | padding: 32px 24px; |
| | |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 32px; |
| | | |
| | | .price-card { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .price { |
| | | |
| | | |
| | | font-size: 48px; |
| | | line-height: 40px; |
| | | letter-spacing: normal; |
| | |
| | | } |
| | | |
| | | .old-price { |
| | | |
| | | |
| | | font-size: 28px; |
| | | font-weight: normal; |
| | | line-height: 40px; |
| | |
| | | } |
| | | |
| | | .trip-map { |
| | | margin: 32px 0; |
| | | width: 622px; |
| | | margin-bottom: 32px; |
| | | width: 100%; |
| | | height: 270px; |
| | | border-radius: 16px; |
| | | background: #F2F3F7; |
| | |
| | | if (status == 8) { |
| | | return '待支付' |
| | | } |
| | | if (status >= 9) { |
| | | if (status == 9) { |
| | | return '待评价' |
| | | } |
| | | if (status == 10) { |
| | | return '已完成' |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | if (status === 0) { |
| | | setPolyline([]); |
| | | setPolyline(routePath); |
| | | return; |
| | | } |
| | | const polylineData = [{ |
| | |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | if (!polyline.length) return |
| | | let viewportPoints = polyline; |
| | | if (polyline.length == 1) { |
| | | viewportPoints = polyline[0].points; |
| | | } |
| | | const mapCtx = Taro.createMapContext('trip-map'); |
| | | Taro.nextTick(() => { |
| | | mapCtx.includePoints({ |
| | | points: polyline, |
| | | padding: [80, 80, 80, 80] |
| | | points: viewportPoints, |
| | | padding: [50, 30, 10, 30] |
| | | }); |
| | | }); |
| | | }, [polyline, markers]) |
| | |
| | | orderData.status != 0 && |
| | | <View className='trip-top'> |
| | | <View className='price-card'> |
| | | <Text className='price'>{orderData.payMoney || '0.00'}元</Text> |
| | | <Text className='price'>{orderData.payMoney || '0.00'}</Text> |
| | | <Text className='unit'>元</Text> |
| | | {orderData.orderMoney > orderData.payMoney && ( |
| | | <Text className='old-price'>{orderData.orderMoney || '-'}元</Text> |
| | |
| | | id="trip-map" |
| | | longitude={longitude} |
| | | latitude={latitude} |
| | | scale={14} |
| | | markers={markers} |
| | | polyline={polyline} |
| | | showLocation={false} |
| | | style={{ borderRadius: '16px' }} |
| | | style={{ borderRadius: '16px', width: '100%' }} |
| | | /> |
| | | </View> |
| | | {/* 行程信息 */} |
| | |
| | | if (status == 8) { |
| | | return '待支付' |
| | | } |
| | | if (status >= 9) { |
| | | if (status == 9) { |
| | | return '待评价' |
| | | } |
| | | if (status == 10) { |
| | | return '已完成' |
| | | } |
| | | } |
| | |
| | | |
| | | Taro.setStorageSync('endSite', selectedSite) |
| | | Taro.reLaunch({ |
| | | url: '/robotaxi/pages/begin-route/index', |
| | | url: `/robotaxi/pages/begin-route/index?skipPageFlag=1`, |
| | | }) |
| | | } |
| | | } |
| | |
| | | } catch { } |
| | | Taro.setStorageSync('endSite', item) |
| | | Taro.reLaunch({ |
| | | url: '/robotaxi/pages/begin-route/index', |
| | | url: '/robotaxi/pages/begin-route/index?skipPageFlag=1', |
| | | }) |
| | | } |
| | | } |
| | |
| | | this.socketTask.onMessage((res) => { |
| | | try { |
| | | const data = JSON.parse(res.data as string); |
| | | if (data?.type !== 'pong') { |
| | | console.log('收到 WebSocket 消息:', res.data); |
| | | } |
| | | const content = JSON.parse(data.content || '{}'); |
| | | this.handleIncomingMessage(data, content); |
| | | if (data.type) { |
| | |
| | | Taro.onSocketMessage((res) => { |
| | | try { |
| | | const data = JSON.parse(res.data as string); |
| | | if (data?.type !== 'pong') { |
| | | console.log('收到 WebSocket 消息:', res.data); |
| | | } |
| | | const content = JSON.parse(data.content || '{}'); |
| | | this.handleIncomingMessage(data, content); |
| | | if (data.type) { |