import Taro, { useRouter, useDidHide } from '@tarojs/taro';
|
import { useState, useEffect, useRef } from 'react';
|
import { View, Text, Image, Map } from '@tarojs/components';
|
import './index.less';
|
import { getOrderDetail } from '../../../api/order';
|
import EvaluatePopup from '../../begin-route/components/evaluate-popup';
|
import { submitOrderComment } from '../../../api/begin-route';
|
import { getOrderDistance } from '../../../api/select-map-site';
|
import { formatTripDistance, formatTripDurationFromSeconds, safeParse } from '../../../../utils/Robotaxi-public-method';
|
import { getOrderId } from '../../../utils/order';
|
import { withRobotaxiWhitelist } from '../../../../utils/whitelist';
|
import { showToast } from '../../../components/toast';
|
import { withToast } from '../../../components/toast/with-toast';
|
|
function OrderDetailPage() {
|
const router = useRouter();
|
const { orderId } = router.params;
|
|
const good = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/good.png';
|
const normal = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/normal.png';
|
const bad = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/bad.png';
|
const activeGood = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-good.png';
|
const activeBad = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-bad.png';
|
const activeNormal = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-normal.png';
|
|
const [activeIndex, setActiveIndex] = useState<number | null>(null) // 默认选中"非常满意"
|
const [orderData, setOrderData] = useState<any>(null);
|
const [showEvaluatePopup, setShowEvaluatePopup] = useState(false);
|
const [selectedOrderRating, setSelectedOrderRating] = useState(1); // 当前准备提交的评价分值
|
const [evaluatePopupStatus, setEvaluatePopupStatus] = useState(0); // 0 不弹评价弹窗,1 点击任意评价都弹窗
|
const showEvaluatePopupRef = useRef(false);
|
|
// 地图相关状态
|
const [longitude, setLongitude] = useState(116.397128) // 地图中心点经度
|
const [latitude, setLatitude] = useState(39.916527) // 地图中心点纬度
|
const [scale, setScale] = useState(12) // 地图缩放级别,默认 12 以展示较大范围
|
const [markers, setMarkers] = useState<any[]>([]) // 起点和终点标记
|
const [polyline, setPolyline] = useState<any[]>([]) // 行驶路径
|
|
const getPointsCenter = (points: Array<{ longitude: number; latitude: number }>) => {
|
if (!points.length) {
|
return null;
|
}
|
let minLng = points[0].longitude;
|
let maxLng = points[0].longitude;
|
let minLat = points[0].latitude;
|
let maxLat = points[0].latitude;
|
points.forEach((point) => {
|
minLng = Math.min(minLng, point.longitude);
|
maxLng = Math.max(maxLng, point.longitude);
|
minLat = Math.min(minLat, point.latitude);
|
maxLat = Math.max(maxLat, point.latitude);
|
});
|
return { longitude: (minLng + maxLng) / 2, latitude: (minLat + maxLat) / 2 };
|
};
|
|
const normalizePoint = (longitude: any, latitude: any) => {
|
const lon = Number(longitude);
|
const lat = Number(latitude);
|
if (Number.isNaN(lon) || Number.isNaN(lat)) return null;
|
if (Math.abs(lon) > 180 || Math.abs(lat) > 90) return null;
|
if (lon === 0 && lat === 0) return null;
|
return { longitude: lon, latitude: lat };
|
};
|
|
useEffect(() => {
|
if (orderId) {
|
fetchOrderDetail(orderId);
|
}
|
}, [orderId]);
|
|
useEffect(() => {
|
const fetchEvaluatePopupConfig = async () => {
|
try {
|
const res: any = await getOrderDistance();
|
setEvaluatePopupStatus(Number(res?.evaluatePopupStatus) === 1 ? 1 : 0);
|
} catch (error) {
|
setEvaluatePopupStatus(0);
|
}
|
};
|
fetchEvaluatePopupConfig();
|
}, []);
|
|
useEffect(() => {
|
showEvaluatePopupRef.current = showEvaluatePopup;
|
}, [showEvaluatePopup]);
|
|
useDidHide(() => {
|
if (showEvaluatePopupRef.current) {
|
return;
|
}
|
setMarkers([]);
|
setPolyline([]);
|
setLongitude(116.397128);
|
setLatitude(39.916527);
|
setOrderData(null);
|
setShowEvaluatePopup(false);
|
setActiveIndex(null);
|
});
|
|
const getNavTitle = (status: number) => {
|
if (status == 0) {
|
return '已取消'
|
}
|
if (status >= 1 && status <= 7) {
|
return '进行中'
|
}
|
if (status == 8) {
|
return '待支付'
|
}
|
if (status == 9) {
|
return '待评价'
|
}
|
if (status == 10) {
|
return '已完成'
|
}
|
}
|
|
const fetchOrderDetail = async (id: string) => {
|
console.log('订单ID:', id);
|
try {
|
const res = await getOrderDetail(id);
|
Taro.setNavigationBarTitle({ title: getNavTitle(res.status) || '订单详情' });
|
setOrderData(res);
|
initMap(res);
|
setActiveIndex(res.orderRating);
|
} catch (error) {
|
console.error('获取订单详情失败', error);
|
showToast({ title: '获取详情失败', icon: 'none' });
|
}
|
};
|
|
const initMap = (data: OrderDetail) => {
|
const { startLat, startLon, endLat, endLon, trajectories, status } = data;
|
const startPoint = normalizePoint(startLon, startLat);
|
const endPoint = normalizePoint(endLon, endLat);
|
|
// 设置起点和终点标记
|
const markerList = [
|
startPoint ? {
|
id: 1,
|
longitude: startPoint.longitude,
|
latitude: startPoint.latitude,
|
width: 20,
|
height: 36,
|
anchor: { x: 0.5, y: 1 },
|
iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/start-text.png',
|
} : null,
|
endPoint ? {
|
id: 2,
|
longitude: endPoint.longitude,
|
latitude: endPoint.latitude,
|
width: 20,
|
height: 36,
|
anchor: { x: 0.5, y: 1 },
|
iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/end-text.png',
|
} : null
|
].filter(Boolean) as any[];
|
setMarkers(markerList);
|
|
// 处理行驶路径
|
const routePath = trajectories?.length > 0
|
? trajectories
|
.map(item => {
|
const [lon, lat] = String(item.location || '').split(',');
|
return normalizePoint(lon, lat);
|
})
|
.filter(Boolean) as Array<{ longitude: number; latitude: number }>
|
: [startPoint, endPoint].filter(Boolean) as Array<{ longitude: number; latitude: number }>;
|
|
// 设置地图中心点,确保地图移动到目标区域
|
const center = getPointsCenter(routePath);
|
if (center) {
|
setLongitude(center.longitude);
|
setLatitude(center.latitude);
|
// 稍微缩小一点,让用户能看到更多周围信息,减少后续 includePoints 的跳动感
|
setScale(12);
|
}
|
|
if (status === 0) {
|
setPolyline(routePath);
|
return;
|
}
|
const polylineData = [{
|
points: routePath,
|
color: '#6D7BC1',
|
width: 10,
|
dottedLine: false,
|
arrowLine: false,
|
},
|
{
|
points: routePath,
|
color: '#7B91FF',
|
width: 8,
|
dottedLine: false,
|
arrowLine: true,
|
}];
|
setPolyline(polylineData);
|
};
|
|
useEffect(() => {
|
if (!polyline.length && !markers.length) return
|
let viewportPoints: any[] = [];
|
if (polyline.length > 0) {
|
if (polyline[0].points) {
|
viewportPoints = viewportPoints.concat(polyline[0].points);
|
} else if (Array.isArray(polyline)) {
|
viewportPoints = viewportPoints.concat(polyline);
|
}
|
}
|
if (markers.length > 0) {
|
viewportPoints = viewportPoints.concat(markers);
|
}
|
// 过滤数据,只保留 latitude 和 longitude
|
const purePoints = viewportPoints.map(p => ({
|
latitude: p.latitude,
|
longitude: p.longitude
|
}));
|
|
const mapCtx = Taro.createMapContext('trip-map');
|
// 适当增加延迟,确保 Map 组件渲染完成后再调用 includePoints
|
setTimeout(() => {
|
mapCtx.includePoints({
|
points: purePoints,
|
padding: [50, 100, 20, 100],
|
success: () => console.log('includePoints success'),
|
fail: (err) => console.error('includePoints failed', err)
|
});
|
}, 200);
|
}, [polyline, markers])
|
|
const todescription = () => {
|
showEvaluatePopupRef.current = true;
|
Taro.navigateTo({
|
url: `/robotaxi/pages/expense-description/index?orderId=${orderId}`,
|
})
|
}
|
|
const handleOpenEvaluatePopup = () => {
|
setShowEvaluatePopup(true);
|
}
|
const handleCloseEvaluatePopup = () => {
|
setShowEvaluatePopup(false);
|
setActiveIndex(null);
|
}
|
const handleConfirmEvaluate = () => {
|
setActiveIndex(selectedOrderRating);
|
setShowEvaluatePopup(false);
|
}
|
|
const submitSimpleEvaluate = (index: number) => {
|
const targetOrderId = getOrderId(orderId);
|
if (!targetOrderId) {
|
showToast({ title: '订单信息缺失,无法提交评价', icon: 'none' });
|
return;
|
}
|
const successToastMap: Record<number, string> = {
|
1: '收到您的评价,我们将努力提升体验',
|
2: '收到您的评价,我们将努力提升体验',
|
3: '感谢您的反馈,欢迎下次乘坐'
|
};
|
submitOrderComment({ orderRating: index, orderId: targetOrderId })
|
.then(() => {
|
setActiveIndex(index);
|
showToast({
|
title: successToastMap[index] || '评价提交成功',
|
icon: 'none'
|
});
|
})
|
.catch(() => {
|
showToast({ title: '评价提交失败,请稍后重试', icon: 'none' });
|
});
|
}
|
|
const changeEvaluate = (index: number) => {
|
if (activeIndex !== null) return;
|
if (evaluatePopupStatus === 1) {
|
// 配置开启时,任意评价项都走弹窗反馈链路。
|
setSelectedOrderRating(index);
|
handleOpenEvaluatePopup();
|
return;
|
}
|
submitSimpleEvaluate(index);
|
}
|
|
if (!orderData) return null;
|
|
return (
|
<View className='order-detail-page'>
|
{/* 车辆信息 */}
|
{
|
(orderData.vehicleId || orderData.status >= 9) &&
|
<View className='car-info'>
|
{
|
orderData.vehicleId &&
|
<View className='info-top'>
|
<View>
|
<View className='car-name'>{orderData.plateNumber}</View>
|
<View className='info'>
|
<Text className='car-color'>小鹏GX</Text>
|
<Text className='info-line'></Text>
|
<Text className='car-color'>{orderData.vehicleColor}</Text>
|
{/* <Text className='car-code'>车身编码: </Text>
|
<Text className='code'>{orderData.vehicleCode}</Text> */}
|
</View>
|
</View>
|
<Image src={safeParse(orderData.vehiclePictures, {})?.url || 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/default-vehicle-img-20260724-v2.png'} className='car-img' />
|
</View>
|
}
|
{
|
orderData.status >= 9 &&
|
<View className='evaluate-card'>
|
<View className='card-title'>您对本次行程满意吗?</View>
|
<View className='card-content'>
|
<View className={`evaluate-item ${activeIndex == 1 ? 'active' : ''}`} onClick={() => changeEvaluate(1)}>
|
<Image src={activeIndex == 1 ? activeBad : bad} className='evaluate-img' />
|
<Text className={`evaluate-text ${activeIndex == 1 ? 'active-text' : ''}`}>非常不满意</Text>
|
</View>
|
<View className={`evaluate-item ${activeIndex == 2 ? 'active' : ''}`} onClick={() => changeEvaluate(2)}>
|
<Image src={activeIndex == 2 ? activeNormal : normal} className='evaluate-img' />
|
<Text className={`evaluate-text ${activeIndex == 2 ? 'active-text' : ''}`}>一般般</Text>
|
</View>
|
<View className={`evaluate-item ${activeIndex == 3 ? 'active' : ''}`} onClick={() => changeEvaluate(3)}>
|
<Image src={activeIndex == 3 ? activeGood : good} className='evaluate-img' />
|
<Text className={`evaluate-text ${activeIndex == 3 ? 'active-text' : ''}`}>非常满意</Text>
|
</View>
|
</View>
|
</View>
|
}
|
</View>
|
}
|
{/* 行程信息 */}
|
<View className='trip-info'>
|
{
|
orderData.status != 0 &&
|
<View className='trip-top'>
|
<View className='price-card'>
|
<Text className='price'>{orderData.payMoney || '0.00'}</Text>
|
<Text className='unit'>元</Text>
|
{orderData.orderMoney > orderData.payMoney && (
|
<Text className='old-price'>{orderData.orderMoneyStr || '-'}元</Text>
|
)}
|
</View>
|
<View onClick={todescription} className='price-remark'>
|
<Text className='remark-text'>费用说明</Text>
|
<Image src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/right.png" className='remark-img' />
|
</View>
|
</View>
|
}
|
{/* 地图-行驶路径 */}
|
<View className='trip-map'>
|
<Map
|
id="trip-map"
|
longitude={longitude}
|
latitude={latitude}
|
scale={scale}
|
markers={markers}
|
polyline={polyline}
|
showLocation={false}
|
style={{ borderRadius: '16px', width: '100%' }}
|
/>
|
</View>
|
{/* 行程信息 */}
|
<View className='trip-line'>
|
{
|
orderData.status != 0 &&
|
<View className='line-name'>全程{formatTripDistance(orderData.totalMileage)}|{formatTripDurationFromSeconds(orderData.orderTime)}</View>
|
}
|
<View className='line-address'>
|
<View className='line-icon first-icon'></View>
|
<Text className='line-text'>{orderData.startAddress}</Text>
|
</View>
|
<View className='line-address'>
|
<View className='line-icon'></View>
|
<Text className='line-text'>{orderData.endAddress}</Text>
|
</View>
|
</View>
|
</View>
|
|
<EvaluatePopup
|
show={showEvaluatePopup}
|
orderId={orderId}
|
orderRating={selectedOrderRating}
|
onClose={handleCloseEvaluatePopup}
|
onConfirm={handleConfirmEvaluate}
|
/>
|
</View>
|
)
|
}
|
|
export default withRobotaxiWhitelist(withToast(OrderDetailPage));
|