feat(robotaxi): 更新UI资源、样式和功能逻辑
- 替换和更新多个图片资源,删除无用资源,新增按钮图标
- 调整页面样式细节,包括字体、间距、布局和颜色
- 优化订单详情页,修改标题和评价逻辑
- 改进地图页面,调整定位和区域显示逻辑
- 完善优惠券页面,调整状态显示和时间格式
- 增强行程相关组件,优化倒计时和取消逻辑
- 修复站点搜索功能,增加距离校验和提示弹窗
- 统一代码格式,修复部分样式文件结尾
| | |
| | | .select-city { |
| | | position: fixed; |
| | | top: 124px; |
| | | position: absolute; |
| | | top: 104px; |
| | | left: 40px; |
| | | display: flex; |
| | | align-items: center; |
| | |
| | | |
| | | .pointer-icon { |
| | | width: 52px; |
| | | height: 76px; |
| | | height: 76px; |
| | | margin-top: -54px; |
| | | |
| | | &.bounce { |
| | |
| | | |
| | | .content-box-top { |
| | | padding-bottom: 102px; |
| | | width: fit-content; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | .origin-box { |
| | |
| | | } |
| | | |
| | | .position-box { |
| | | width: 100%; |
| | | |
| | | .is-operating-area { |
| | | margin: 0 auto; |
| | |
| | | } |
| | | |
| | | .now-site-icon { |
| | | flex-shrink: 0; |
| | | margin-left: 2px; |
| | | width: 32px; |
| | | height: 32px; |
| | |
| | | margin-left: 16px; |
| | | |
| | | view:first-child { |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 28px; |
| | | font-weight: 600; |
| | | line-height: 44px; |
| | |
| | | margin-bottom: 24px; |
| | | border: 2px solid #fff; |
| | | border-radius: 32px; |
| | | height: 190px; |
| | | |
| | | .activity-img { |
| | | border-radius: 32px; |
| | | width: 100%; |
| | | height: 190px; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | |
| | | console.log('getSetting获取scope.userLocation授权状态', auth); |
| | | if (auth == undefined) { |
| | | getLocationInfo(); |
| | | skipCityChangeEffectRef.current = true; |
| | | // skipCityChangeEffectRef.current = true; |
| | | return; |
| | | } |
| | | if (auth === false) { |
| | |
| | | setPolygons(newPolygons); |
| | | // if ((orderData || orderData?.id) && JSON.stringify(orderData) != '{}') return; |
| | | // 定位到第一个运营区域的第一个点 |
| | | const firstPolygon = res[0]; |
| | | if (firstPolygon && firstPolygon.points && firstPolygon.points.length > 0) { |
| | | const firstPoint = firstPolygon.points[0]; |
| | | setLatitude(Number(firstPoint.lat)); |
| | | setLongitude(Number(firstPoint.lng)); |
| | | skipNextLocationCenterRef.current = true; |
| | | shouldIgnoreRegionChange.current = true; |
| | | // const firstPolygon = res[0]; |
| | | // if (firstPolygon && firstPolygon.points && firstPolygon.points.length > 0) { |
| | | // const firstPoint = firstPolygon.points[0]; |
| | | // setLatitude(Number(firstPoint.lat)); |
| | | // setLongitude(Number(firstPoint.lng)); |
| | | // skipNextLocationCenterRef.current = true; |
| | | // shouldIgnoreRegionChange.current = true; |
| | | // const mapCtx = Taro.createMapContext('map'); |
| | | // mapCtx.moveToLocation({ |
| | | // latitude: Number(firstPoint.lat), |
| | | // longitude: Number(firstPoint.lng), |
| | | // }); |
| | | // } |
| | | if (!Taro.getStorageSync('startSite')) { |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | mapCtx.moveToLocation({ |
| | | latitude: Number(firstPoint.lat), |
| | | longitude: Number(firstPoint.lng), |
| | | const points = extractPointsFromPolygons(newPolygons); |
| | | mapCtx.includePoints({ |
| | | points: points, |
| | | padding: [100, 100, 100, 100] |
| | | }); |
| | | } |
| | | } else { |
| | |
| | | |
| | | // 更新地图数据 |
| | | const updateMapData = async (lat: number, lng: number) => { |
| | | const { isInside, electronicAreaId, newMarkers, sites } = await fetchMapData(lat, lng); |
| | | const selectedCity = Taro.getStorageSync('selectedCity'); |
| | | const { isInside, electronicAreaId, newMarkers, sites } = await fetchMapData(lat, lng, selectedCity?.code); |
| | | |
| | | setIsOperatingArea(isInside); |
| | | setElectronicAreaId(electronicAreaId); |
| | |
| | | queryTemperatureByOrderId, |
| | | queryOrderFinishTime, |
| | | queryFareBreakdown, |
| | | submitOrderComment |
| | | submitOrderComment, |
| | | cancelOrder |
| | | } from '../../../api/begin-route'; |
| | | |
| | | type Props = { |
| | | tripStatus?: number; // 行程状态 |
| | | handleOpenCancelPopup?: () => void; // 打开取消弹窗 |
| | | handleCancelTimeout?: () => void; // 取消超时处理 |
| | | handleFinishTimeout?: () => void; // 到达目的地倒计时结束处理 |
| | | handleOpenOverRoutePopup?: () => void; // 打开超出行程时间弹窗 |
| | | handlePay?: () => void; // 支付处理 |
| | | updateStatus?: () => void; // 更新行程状态 |
| | |
| | | orderId?: string; // 订单ID |
| | | } |
| | | |
| | | export default function CallingCar({ tripStatus = 3, handleOpenCancelPopup, handleCancelTimeout, handleOpenOverRoutePopup, handlePay, updateStatus, handleOpenEvaluatePopup, handleOpenAnnouncementsPopup, estimatedWaitMinutes, routeDurationSeconds, vehicleInfo, orderId }: Props) { |
| | | export default function CallingCar({ tripStatus = 3, handleOpenCancelPopup, handleCancelTimeout, handleFinishTimeout, handleOpenOverRoutePopup, handlePay, updateStatus, handleOpenEvaluatePopup, handleOpenAnnouncementsPopup, estimatedWaitMinutes, routeDurationSeconds, vehicleInfo, orderId }: Props) { |
| | | |
| | | const [trunkStatus, setTrunkStatus] = useState(false); // 后备箱状态 |
| | | const [lockStatus, setLockStatus] = useState(false); // 锁车状态 |
| | |
| | | return '普通时段'; |
| | | }; |
| | | const durationDisplay = formatDurationDisplay(displayRouteDurationSeconds); |
| | | const isArriveSoon = tripStatus === 4 && typeof displayRouteDurationSeconds === 'number' && !Number.isNaN(displayRouteDurationSeconds) && displayRouteDurationSeconds < 60; |
| | | const isArriveSoon = tripStatus === 4 && ((typeof displayRouteDurationSeconds === 'number' && !Number.isNaN(displayRouteDurationSeconds) && displayRouteDurationSeconds <= 60) || !displayRouteDurationSeconds); |
| | | const titleMap = { |
| | | 3: <>预计<View className="time">{waitMinutes}</View>分钟内有车应答</>, |
| | | 4: <>车辆还有<View className="time">{durationDisplay.value}</View>{durationDisplay.unit}到达</>, |
| | |
| | | if (remaining !== null && remaining <= 0) { |
| | | if (!countdownFinishedRef.current) { |
| | | countdownFinishedRef.current = true; |
| | | handleCancelTimeout?.(); |
| | | const targetOrderId = orderId || Taro.getStorageSync('orderId'); |
| | | if (targetOrderId) { |
| | | cancelOrder(targetOrderId).finally(() => { |
| | | handleCancelTimeout?.(); |
| | | }); |
| | | } else { |
| | | handleCancelTimeout?.(); |
| | | } |
| | | } |
| | | if (countdownTimerRef.current) { |
| | | clearTimeout(countdownTimerRef.current); |
| | |
| | | clearTimeout(finishCountdownTimerRef.current); |
| | | } |
| | | setFinishCountdownSeconds(0); |
| | | handleFinishTimeout?.(); |
| | | return; |
| | | } |
| | | setFinishCountdownSeconds(remaining); |
| | |
| | | }).then((res: any) => { |
| | | setIsEvaluated(true); |
| | | Taro.showToast({ |
| | | title: '评价已提交', |
| | | title: ['收到您的评价,我们将努力提升体验', '感谢您的反馈,欢迎下次乘坐'][index - 2], |
| | | icon: 'none' |
| | | }); |
| | | }).catch(() => { |
| | |
| | | <View className='calling-car-content-title'>{titleMap[tripStatus]}</View> |
| | | )} |
| | | {descMap[tripStatus] && ( |
| | | <View style={{paddingBottom: tripStatus == 3 ? '20px' : '0px'}} className={descMap[tripStatus].className}>{descMap[tripStatus].text}</View> |
| | | <View style={{ paddingBottom: tripStatus == 3 ? '20px' : '0px' }} className={descMap[tripStatus].className}>{descMap[tripStatus].text}</View> |
| | | )} |
| | | </View> |
| | | { |
| | | [3, 4, 5].includes(tripStatus) && |
| | | <View onClick={handleOpenCancelPopup} className="cancel">取消行程</View> |
| | | <Image onClick={handleOpenCancelPopup} src={require('../../../assets/cancel-btn.png')} className="cancel" /> |
| | | } |
| | | </View> |
| | | { |
| | |
| | | </View> |
| | | : |
| | | <View className='cancel-btn-box'> |
| | | <View className='cancel-btn-cancel' onClick={onConfirm}>确认取消</View> |
| | | <Image className='cancel-btn-cancel' onClick={onConfirm} src={require('../../../assets/submit-cancel-btn.png')} /> |
| | | <View className='cancel-btn-again' onClick={onClose}>再等等</View> |
| | | </View> |
| | | } |
| | |
| | | |
| | | &-title { |
| | | font-size: 36px; |
| | | font-weight: bold; |
| | | font-weight: 500; |
| | | line-height: 36px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.9); |
| | |
| | | align-items: center; |
| | | |
| | | .time { |
| | | font-family: 'Figtree-SemiBold'; |
| | | margin: 0 4px; |
| | | font-size: 40px; |
| | | font-weight: 600; |
| | |
| | | } |
| | | |
| | | .cancel { |
| | | padding: 0 24px; |
| | | border-radius: 3112px; |
| | | border: 2px solid #181916; |
| | | box-sizing: border-box; |
| | | font-size: 24px; |
| | | flex-shrink: 0; |
| | | width: 144px; |
| | | height: 48px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | letter-spacing: normal; |
| | | color: #181916; |
| | | } |
| | | } |
| | | |
| | |
| | | justify-content: space-between; |
| | | |
| | | &-num { |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 48px; |
| | | font-weight: 600; |
| | | line-height: 64px; |
| | |
| | | align-items: center; |
| | | |
| | | view:nth-child(1) { |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 48px; |
| | | font-weight: 600; |
| | | line-height: 40px; |
| | |
| | | } |
| | | |
| | | view:nth-child(3) { |
| | | font-family: 'Figtree-Light'; |
| | | font-size: 28px; |
| | | line-height: 36px; |
| | | letter-spacing: normal; |
| | |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | overflow:auto; |
| | | overflow: auto; |
| | | |
| | | max-height: 0; |
| | | margin-top: 0; |
| | |
| | | padding-top: 22px; |
| | | |
| | | &-text { |
| | | font-family: 'Figtree-Light'; |
| | | font-size: 28px; |
| | | line-height: 44px; |
| | | letter-spacing: normal; |
| | |
| | | border: 3px solid #FFFFFF; |
| | | |
| | | &-title { |
| | | font-weight: 500; |
| | | padding-left: 32px; |
| | | font-size: 28px; |
| | | line-height: 40px; |
| | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | .cancel-tip-box { |
| | | margin-top: 4px; |
| | | |
| | | .cancel-tip-box-title{ |
| | | .cancel-tip-box-title { |
| | | font-size: 36px; |
| | | font-weight: bold; |
| | | line-height: normal; |
| | |
| | | |
| | | .cancel-btn-cancel { |
| | | flex: 1; |
| | | line-height: 96px; |
| | | text-align: center; |
| | | border-radius: 16px; |
| | | box-sizing: border-box; |
| | | border: 2px solid #181916; |
| | | font-size: 30px; |
| | | letter-spacing: normal; |
| | | color: #181916; |
| | | height: 96px; |
| | | } |
| | | |
| | | .cancel-btn-again { |
| | |
| | | } |
| | | |
| | | .money-num { |
| | | font-family: 'Figtree-Light'; |
| | | font-family: 'Figtree-SemiBold'; |
| | | margin: 0 4px; |
| | | font-size: 48px; |
| | | font-weight: 600; |
| | |
| | | } |
| | | |
| | | .original-fare { |
| | | font-family: 'Figtree-Light'; |
| | | font-size: 28px; |
| | | font-weight: normal; |
| | | line-height: 40px; |
| | |
| | | }); |
| | | return; |
| | | } |
| | | if (!feedbackContent.trim()) { |
| | | Taro.showToast({ |
| | | title: '请输入问题描述', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | setIsSubmitting(true); |
| | | const submitter = submitMode === 'complaint' ? submitComplaint : submitOrderComment; |
| | | submitter({ |
| | |
| | | {/* <Image className='evaluate-problem-pic-box-img /> */} |
| | | <View className='evaluate-problem-pic-box-upload'> |
| | | <Image className='evaluate-problem-pic-box-upload-img' src={require('../../../assets/upload-icon.png')} /> |
| | | <View className='evaluate-problem-pic-box-upload-count'>0/9</View> |
| | | <View className='evaluate-problem-pic-box-upload-count'>0/3</View> |
| | | </View> |
| | | </View> |
| | | </View> |
| | |
| | | |
| | | .container { |
| | | width: 100%; |
| | | position: absolute; |
| | | position: fixed; |
| | | bottom: 0; |
| | | z-index: 10; |
| | | |
| | |
| | | background: rgba(255, 255, 255, 0.9); |
| | | padding: 12px 12px 12px 20px; |
| | | min-width: 155px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .waiting-callout-txt-content { |
| | |
| | | import suggest from '../../../robotaxi/assets/suggest.png' |
| | | import originIcon from '../../../robotaxi/assets/origin-icon.png' |
| | | import wsManager from '../../../utils/websocket'; |
| | | import { getWalkingRoute, formatDurationToClock, formatDateTime, clearOrderTripStorage, preloadImageToTemp } from '../../../utils/Robotaxi-public-method'; |
| | | import { getWalkingRoute, formatDurationToClock, formatDateTime, clearOrderTripStorage, preloadImageToTemp, debounce } from '../../../utils/Robotaxi-public-method'; |
| | | import { |
| | | saveOrder, |
| | | queryRouteAndFare, |
| | |
| | | getOrderPayment, |
| | | cancelOrder, |
| | | queryOrderRoute, |
| | | endOrder, |
| | | } from '../../../robotaxi/api/begin-route'; |
| | | import { getOrderDetail } from '../../../robotaxi/api/order'; |
| | | import { checkOtp } from '../../../utils/tools'; |
| | |
| | | latitude: e.latitude, |
| | | longitude: e.longitude, |
| | | }], |
| | | padding: [50, 50, 600, 50] |
| | | padding: [50, 50, 800, 50] |
| | | }); |
| | | } |
| | | setElapsedSeconds(0); |
| | |
| | | Taro.removeStorageSync(WAITING_TIMER_ELAPSED_KEY); |
| | | } else if ([8, 9].includes(tripStatus)) { |
| | | getOrderDetail(Taro.getStorageSync('orderId')).then((res: any) => { |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | if (res.trajectories) { |
| | | const points = res.trajectories.map((item: any) => ({ |
| | | latitude: item.location.split(',')[1], |
| | | longitude: item.location.split(',')[0], |
| | | })); |
| | | applyRoutePoints(points); |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | mapCtx.includePoints({ |
| | | points: points, |
| | | padding: [40, 40, 500, 40] |
| | | }) |
| | | } else { |
| | | mapCtx.moveToLocation({ |
| | | latitude: 39.916527, |
| | | longitude: 116.397128, |
| | | }); |
| | | } |
| | | }); |
| | | setElapsedSeconds(0); |
| | |
| | | |
| | | const setPopupVisible = (setter: (value: boolean) => void, visible: boolean) => () => setter(visible); |
| | | const handleCloseCustomerService = setPopupVisible(setShowCustomerService, false); |
| | | const openService = setPopupVisible(setShowCustomerService, true); |
| | | |
| | | const openService = () => { |
| | | const phoneNumber = Taro.getStorageSync('customerServicePhone'); |
| | | Taro.makePhoneCall({ |
| | | phoneNumber, |
| | | }) |
| | | } |
| | | |
| | | // 立即呼叫 |
| | | const callImmediately = async () => { |
| | | const callImmediately = debounce(async () => { |
| | | const otp = await checkOtp(false); |
| | | if (!otp) { |
| | | jumpTo({ url: '/pages/user/login?hideBack=1' }); |
| | |
| | | wsManager.on('vehicle_location', vehicleLocationHandlerRef.current); |
| | | vehicleLocationBound.current = true; |
| | | } |
| | | } |
| | | }, 500) |
| | | |
| | | // 确认取消 |
| | | const handleConfirmCancel = async () => { |
| | |
| | | const handleCloseCancelPopup = setPopupVisible(setShowCancelPopup, false); |
| | | const handleOpenCancelPopup = setPopupVisible(setShowCancelPopup, true); |
| | | const handleOpenCancelPopupWithType = (type: number) => { |
| | | setOrderId(''); |
| | | clearOrderTripStorage(); |
| | | setCancelType(type); |
| | | setShowCancelPopup(true); |
| | | setTripStatus(1); |
| | | updateWalkingRouteForStart(); |
| | | const s = Taro.getStorageSync('startSite'); |
| | | const e = Taro.getStorageSync('endSite'); |
| | | if (s?.latitude && s?.longitude && e?.longitude && e?.latitude) { |
| | | requestRouteAndFare(s.latitude, s.longitude, e.latitude, e.longitude, true); |
| | | } |
| | | }; |
| | | const handleOpenCancelPopupByTripStatus = (status: number) => { |
| | | if ([2, 3].includes(status)) { |
| | |
| | | // 确认结束行程 |
| | | const handleConfirmOverRoute = () => { |
| | | setShowOverRoutePopup(false); |
| | | setTripStatus(8); |
| | | } |
| | | const handleFinishCountdownTimeout = async () => { |
| | | const targetOrderId = orderId || Taro.getStorageSync('orderId'); |
| | | if (!targetOrderId) return; |
| | | try { |
| | | await endOrder(targetOrderId); |
| | | } catch (error) { |
| | | } |
| | | setTripStatus(8); |
| | | } |
| | | |
| | |
| | | showLocation={!!longitude} |
| | | onCalloutTap={handleCalloutTap} |
| | | onRegionChange={handleRegionChange} |
| | | style={{ width: '100%', height: '100%' }} |
| | | style={{ width: '100%', height: '85vh' }} |
| | | > |
| | | {/* 自定义callout */} |
| | | { |
| | |
| | | vehicleInfo={vehicleInfo} |
| | | handleOpenCancelPopup={() => handleOpenCancelPopupByTripStatus(tripStatus)} |
| | | handleCancelTimeout={() => handleOpenCancelPopupWithType(4)} |
| | | handleFinishTimeout={handleFinishCountdownTimeout} |
| | | handleOpenAnnouncementsPopup={handleOpenAnnouncementsPopup} |
| | | handleOpenOverRoutePopup={handleOpenOverRoutePopup} |
| | | handlePay={handlePay} |
| | |
| | | align-items: center; |
| | | flex-shrink: 0; |
| | | margin-right: 48px; |
| | | min-width: 154px; |
| | | } |
| | | |
| | | .price { |
| | | font-family: Figtree; |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 68px; |
| | | font-weight: 600; |
| | | line-height: 72px; |
| | |
| | | |
| | | export default function CouponPage() { |
| | | const [couponStatus] = useState([ |
| | | { label: '全部', value: '' }, |
| | | // { label: '全部', value: '' }, |
| | | // { label: '待领取', value: 0 }, |
| | | { label: '未使用', value: 1 }, |
| | | { label: '已使用', value: 2 }, |
| | | { label: '已失效', value: 3 }]) |
| | | const [activeStatus, setActiveStatus] = useState<string | number>('') |
| | | const [activeStatus, setActiveStatus] = useState<string | number>(1) |
| | | const [couponList, setCouponList] = useState<CouponItem[]>([]) |
| | | const [animationData, setAnimationData] = useState<any>(null) |
| | | const [visible, setVisible] = useState(false) |
| | |
| | | // 出错时如果是刷新,清空列表 |
| | | if (isRefresh) setCouponList([]); |
| | | } |
| | | } |
| | | const formatTime = (t?: string, type?: string) => { |
| | | const d = new Date(t); |
| | | const y = d.getFullYear(); |
| | | const m = String(d.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(d.getDate()).padStart(2, '0'); |
| | | const h = String(d.getHours()).padStart(2, '0'); |
| | | const min = String(d.getMinutes()).padStart(2, '0'); |
| | | return type == 'list' ? `${y}年${m}月${day}日 ${h}:${min}` : `${y}-${m}-${day}`; |
| | | } |
| | | |
| | | useEffect(() => { |
| | |
| | | } |
| | | |
| | | const toHome = () => { |
| | | Taro.switchTab({ |
| | | url: '/robotaxi/pages/home/index' |
| | | Taro.reLaunch({ |
| | | url: '/pages/robotaxi/home/index' |
| | | }) |
| | | } |
| | | |
| | |
| | | </View> |
| | | <View className='coupon-info'> |
| | | <Text className='name'>{item.title}</Text> |
| | | <Text className='time'>{item.expireTime}到期</Text> |
| | | <Text className='time'>{formatTime(item.expireTime, 'list')}到期</Text> |
| | | <View className='more' onClick={() => openPopup(item)}> |
| | | <Text className='more-detail'>查看详情</Text> |
| | | <Image className='more-img' src={icon}></Image> |
| | |
| | | </View> |
| | | {/* 状态 1-未使用 2-已使用 3-已过期 0待领取 */} |
| | | { |
| | | // item.status === 0 ? ( |
| | | // <View className='receive' onClick={(e) => handleReceive(e, item)}>领取</View> |
| | | // ) : |
| | | item.status === 1 ? ( |
| | | <View onClick={toHome} className='receive' style={{ backgroundColor: '#F58900' }}>去使用</View> |
| | | ) : item.status === 2 ? ( |
| | | <Image className='status-img' src={require('../../assets/coupon-used.png')} /> |
| | | ) : item.status === 3 ? ( |
| | | <Image className='status-img' src={require('../../assets/coupon-lost-efficacy.png')} /> |
| | | ):''} |
| | | // item.status === 0 ? ( |
| | | // <View className='receive' onClick={(e) => handleReceive(e, item)}>领取</View> |
| | | // ) : |
| | | item.status === 1 ? ( |
| | | <View onClick={toHome} className='receive' style={{ backgroundColor: '#F58900' }}>去使用</View> |
| | | ) : item.status === 2 ? ( |
| | | <Image className='status-img' src={require('../../assets/coupon-used.png')} /> |
| | | ) : item.status === 3 ? ( |
| | | <Image className='status-img' src={require('../../assets/coupon-lost-efficacy.png')} /> |
| | | ) : ''} |
| | | </View> |
| | | })} |
| | | </View> |
| | |
| | | {/* 内容文案 */} |
| | | <View className='remark-card'> |
| | | <Text className='remark-label'>有效时间</Text> |
| | | <Text className='remark-value'>{currentCoupon.expireTime} 失效</Text> |
| | | <Text className='remark-value'>{formatTime(currentCoupon.expireTime, 'popup')} 失效</Text> |
| | | </View> |
| | | <View className='remark-card'> |
| | | <Text className='remark-label'>使用时段</Text> |
| | | <Text className='remark-value'>{currentCoupon.timeRule}</Text> |
| | | <Text className='remark-value'>全天</Text> |
| | | </View> |
| | | <View className='remark-card'> |
| | | <Text className='remark-label'>使用地区</Text> |
| | | <Text className='remark-value'> {(currentCoupon.applicableCities || []).join('、')}</Text> |
| | | <Text className='remark-value'> 广州</Text> |
| | | </View> |
| | | <View className='remark-card'> |
| | | <Text className='remark-label'>使用说明</Text> |
| | |
| | | } |
| | | |
| | | .num { |
| | | font-family: Figtree; |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 48px; |
| | | font-weight: 600; |
| | | line-height: 40px; |
| | |
| | | |
| | | .complaint { |
| | | margin-top: 30px; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | .complaint-text { |
| | | padding: 24px 0px; |
| | | box-sizing: border-box; |
| | | border: 2px solid #000000; |
| | | width: 548px; |
| | | height: 92px; |
| | | border-radius: 16px; |
| | | font-family: PingFang SC; |
| | | font-size: 28px; |
| | | font-weight: normal; |
| | | line-height: 44px; |
| | | letter-spacing: 0px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | text-align: center; |
| | | } |
| | | |
| | | margin: 0 auto; |
| | | width: 548px; |
| | | height: 92px; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | .color-red { |
| | | color: #FF4D4F !important; |
| | | } |
| | | } |
| | |
| | | </View> |
| | | )} |
| | | {/* 去投诉 */} |
| | | <View className='complaint' onClick={() => setShowEvaluatePopup(true)}> |
| | | <Text className='complaint-text'>不认可车费,去投诉</Text> |
| | | </View> |
| | | <Image className='complaint' onClick={() => setShowEvaluatePopup(true)} src={require('../../../assets/complaint-btn.png')} /> |
| | | |
| | | {/* 评价弹窗 */} |
| | | <EvaluatePopup |
| | |
| | | } |
| | | |
| | | .num { |
| | | font-family: Figtree; |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 48px; |
| | | font-weight: 600; |
| | | line-height: 40px; |
| | |
| | | |
| | | .color-red { |
| | | color: #FF4D4F !important; |
| | | } |
| | | } |
| | |
| | | export default { |
| | | navigationBarTitleText: '订单详情', |
| | | navigationBarTitleText: '订单已结束', |
| | | }; |
| | |
| | | height: 20px; |
| | | z-index: 1; |
| | | margin: 0 14px; |
| | | background: #D8D8D8; |
| | | } |
| | | |
| | | .car-code { |
| | |
| | | align-items: center; |
| | | |
| | | .price { |
| | | font-family: Figtree; |
| | | font-family: 'Figtree-SemiBold'; |
| | | font-size: 48px; |
| | | font-weight: 600; |
| | | line-height: 40px; |
| | |
| | | } |
| | | |
| | | .old-price { |
| | | font-family: Figtree; |
| | | font-family: 'Figtree-Light'; |
| | | font-size: 28px; |
| | | font-weight: normal; |
| | | line-height: 40px; |
| | |
| | | .line-address { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .line-icon { |
| | | /* 自动布局子元素 */ |
| | | width: 8px; |
| | |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.4); |
| | | } |
| | | .first-icon{ |
| | | |
| | | .first-icon { |
| | | background: #768AFF; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | import activeBad from '../../../assets/active-bad.png' |
| | | import activeNormal from '../../../assets/active-normal.png' |
| | | import right from '../../../assets/right.png' |
| | | import up from '../../../assets/up.png' |
| | | import down from '../../../assets/down.png' |
| | | import lineRight from '../../../assets/line-right.png' |
| | | import { getOrderDetail, OrderDetail } from '../../../api/order'; |
| | | |
| | | // Marker样式配置 |
| | | const MARKER_CONFIG = { |
| | | width: 20, // marker宽度 |
| | | height: 36, // marker高度(包含底部小圆点) |
| | | anchorX: 0.55, // 锚点X(0.5表示居中) |
| | | anchorY: 0.8, // 锚点Y(1表示底部对齐坐标点) |
| | | } |
| | | |
| | | export default function orderDetailPage() { |
| | | const router = useRouter(); |
| | | const { orderId } = router.params; |
| | | |
| | | const [activeIndex, setActiveIndex] = useState(3) // 默认选中"非常满意" |
| | | const [activeIndex, setActiveIndex] = useState<number | null>(null) // 默认选中"非常满意" |
| | | const [orderData, setOrderData] = useState<OrderDetail | null>(null); |
| | | |
| | | // 地图相关状态 |
| | | const [longitude, setLongitude] = useState(104.103731) // 地图中心点经度 |
| | | const [latitude, setLatitude] = useState(30.650722) // 地图中心点纬度 |
| | | const [longitude, setLongitude] = useState(116.397128) // 地图中心点经度 |
| | | const [latitude, setLatitude] = useState(39.916527) // 地图中心点纬度 |
| | | const [markers, setMarkers] = useState<any[]>([]) // 起点和终点标记 |
| | | const [polyline, setPolyline] = useState<any[]>([]) // 行驶路径 |
| | | |
| | |
| | | const res = await getOrderDetail(id); |
| | | setOrderData(res); |
| | | initMap(res); |
| | | if (res.orderRating) { |
| | | setActiveIndex(res.orderRating); |
| | | } |
| | | setActiveIndex(res.orderRating); |
| | | } catch (error) { |
| | | console.error('获取订单详情失败', error); |
| | | Taro.showToast({ title: '获取详情失败', icon: 'none' }); |
| | |
| | | const y = d.getFullYear(); |
| | | const m = String(d.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(d.getDate()).padStart(2, '0'); |
| | | const hh = String(d.getHours()).padStart(2, '0'); |
| | | const mm = String(d.getMinutes()).padStart(2, '0'); |
| | | return `${y}年${m}月${day}日 ${hh}:${mm}`; |
| | | return `${y}年${m}月${day}日`; |
| | | } |
| | | |
| | | const getList = async (page: number, isRefresh = false) => { |
| | |
| | | return (<View key={group.dateLabel || gi}> |
| | | {(group.orders || []).map((item, index) => { |
| | | return <View className='order-item' key={item.orderId || index} onClick={() => toOrderDetail(item)}> |
| | | <View className='order-time'>{`${item.time} ${item.orderTime}` || formatTime(item.time)}</View> |
| | | <View className='order-time'>{formatTime(item.time)}</View> |
| | | <View className='order-start'>起点:{item.originAddress || '-'}</View> |
| | | <View className='order-start'>终点:{item.destinationAddress || '-'}</View> |
| | | <View onClick={(e) => { e.stopPropagation(); againOrder(item) }} className='order-again'> |
| | |
| | | } |
| | | {!isLoading && ( |
| | | <> |
| | | {/* 搜索前 */} |
| | | { |
| | | !searchValue && |
| | | <View className='city-content'> |
| | | <View className='service-city-box'> |
| | | <View className='service-city-title'>运营城市</View> |
| | | <View className='service-city-list'> |
| | | { |
| | | openCityList.map((item, index) => ( |
| | | <View |
| | | key={item.id} |
| | | className={`service-city-item ${selectedCity.name === item.name && 'active-city'}`} |
| | | onClick={() => handleSelectCity(item, true)} |
| | | > |
| | | {item.name} |
| | | </View> |
| | | )) |
| | | } |
| | | </View> |
| | | </View> |
| | | <View className='non-operational-city-box'> |
| | | <View className='non-operational-city-title'>待开放城市</View> |
| | | <ScrollView |
| | | className='non-operational-city-list' |
| | | scrollY |
| | | scrollWithAnimation |
| | | scrollIntoView={scrollIntoView} |
| | | > |
| | | { |
| | | filteredPendingList.map((group) => ( |
| | | <View key={group.title} id={`city-${group.title}`} className='city-group'> |
| | | <View className='city-group-title'>{group.title}</View> |
| | | { |
| | | group.items.map((city, index) => ( |
| | | <View |
| | | key={index} |
| | | className='non-operational-city-item' |
| | | |
| | | > |
| | | {city} |
| | | </View> |
| | | )) |
| | | } |
| | | </View> |
| | | )) |
| | | } |
| | | </ScrollView> |
| | | </View> |
| | | <View className='shortcut'> |
| | | { |
| | | filteredPendingList.map((group) => ( |
| | | <View |
| | | key={group.title} |
| | | className='shortcut-item' |
| | | onClick={(e) => { |
| | | e.stopPropagation(); |
| | | handleScrollTo(group.title); |
| | | }} |
| | | > |
| | | {group.title} |
| | | </View> |
| | | )) |
| | | } |
| | | </View> |
| | | </View> |
| | | } |
| | | {/* 搜索后 */} |
| | | { |
| | | searchValue && |
| | | <View> |
| | | {/* 搜索前 */} |
| | | { |
| | | searchResultList.length > 0 ? |
| | | <View className='search-result-box'> |
| | | { |
| | | searchResultList.map((item, index) => { |
| | | const isOpen = openCityList.some(open => open.name === item.name); |
| | | return ( |
| | | <View key={index} className='search-result-item' onClick={() => handleSelectCity(item, isOpen)}> |
| | | <View className='search-result-city'>{item.name}</View> |
| | | <View className={`search-result-zone ${!isOpen ? 'search-result-zone-disabled' : ''}`}>{isOpen ? '运营区' : '待开放'}</View> |
| | | !searchValue && |
| | | <View className='city-content'> |
| | | <View className='service-city-box'> |
| | | <View className='service-city-title'>运营城市</View> |
| | | <View className='service-city-list'> |
| | | { |
| | | openCityList.map((item, index) => ( |
| | | <View |
| | | key={item.id} |
| | | className={`service-city-item ${selectedCity.name === item.name && 'active-city'}`} |
| | | onClick={() => handleSelectCity(item, true)} |
| | | > |
| | | {item.name} |
| | | </View> |
| | | ) |
| | | }) |
| | | )) |
| | | } |
| | | </View> |
| | | </View> |
| | | <View className='non-operational-city-box'> |
| | | <View className='non-operational-city-title'>待开放城市</View> |
| | | <ScrollView |
| | | className='non-operational-city-list' |
| | | scrollY |
| | | scrollWithAnimation |
| | | scrollIntoView={scrollIntoView} |
| | | > |
| | | { |
| | | filteredPendingList.map((group) => ( |
| | | <View key={group.title} id={`city-${group.title}`} className='city-group'> |
| | | <View className='city-group-title'>{group.title}</View> |
| | | { |
| | | group.items.map((city, index) => { |
| | | const isOpen = openCityList.some(open => open.name === city); |
| | | return ( |
| | | <View |
| | | onClick={() => handleSelectCity({}, isOpen)} |
| | | key={index} |
| | | className='non-operational-city-item' |
| | | |
| | | > |
| | | {city} |
| | | </View> |
| | | ) |
| | | } |
| | | ) |
| | | } |
| | | </View> |
| | | )) |
| | | } |
| | | </ScrollView> |
| | | </View> |
| | | <View className='shortcut'> |
| | | { |
| | | filteredPendingList.map((group) => ( |
| | | <View |
| | | key={group.title} |
| | | className='shortcut-item' |
| | | onClick={(e) => { |
| | | e.stopPropagation(); |
| | | handleScrollTo(group.title); |
| | | }} |
| | | > |
| | | {group.title} |
| | | </View> |
| | | )) |
| | | } |
| | | </View> |
| | | : |
| | | <View className='no-data-box'> |
| | | <Image className='no-data-image' src={require('../../assets/no-data-city.png')} /> |
| | | <View>暂无城市</View> |
| | | </View> |
| | | </View> |
| | | } |
| | | </View> |
| | | } |
| | | {/* 搜索后 */} |
| | | { |
| | | searchValue && |
| | | <View> |
| | | { |
| | | searchResultList.length > 0 ? |
| | | <View className='search-result-box'> |
| | | { |
| | | searchResultList.map((item, index) => { |
| | | const isOpen = openCityList.some(open => open.name === item.name); |
| | | return ( |
| | | <View key={index} className='search-result-item' onClick={() => handleSelectCity(item, isOpen)}> |
| | | <View className='search-result-city'>{item.name}</View> |
| | | <View className={`search-result-zone ${!isOpen ? 'search-result-zone-disabled' : ''}`}>{isOpen ? '运营区' : '待开放'}</View> |
| | | </View> |
| | | ) |
| | | }) |
| | | } |
| | | </View> |
| | | : |
| | | <View className='no-data-box'> |
| | | <Image className='no-data-image' src={require('../../assets/no-data-city.png')} /> |
| | | <View>暂无城市</View> |
| | | </View> |
| | | } |
| | | </View> |
| | | } |
| | | </> |
| | | )} |
| | | </View> |
| | |
| | | |
| | | .content-title { |
| | | font-size: 36px; |
| | | font-weight: bold; |
| | | font-weight: 500; |
| | | line-height: 50px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.9); |
| | |
| | | formatGeofencePolygons, |
| | | fetchMapData, |
| | | getWalkingRoute, |
| | | extractPointsFromPolygons |
| | | } from '../../../utils/Robotaxi-public-method'; |
| | | import { preloadImageToTemp } from '../../../utils/Robotaxi-public-method'; |
| | | import { getGeofence } from '../../api/home'; |
| | | import { getOrderDistance } from '../../api/select-map-site'; |
| | | import { checkOtp } from '../../../utils/tools'; |
| | |
| | | const [userLongitude, setUserLongitude] = useState<number | null>(null); |
| | | const [userLatitude, setUserLatitude] = useState<number | null>(null); |
| | | const [outOfServiceRange, setOutOfServiceRange] = useState(false); |
| | | const tooltipBgUrl = require('../../../robotaxi/assets/tooltip-bg.png'); |
| | | const [tooltipBgSrc, setTooltipBgSrc] = useState(tooltipBgUrl); |
| | | |
| | | const router = Taro.useRouter(); |
| | | |
| | |
| | | setOutOfServiceRange(getOutOfServiceRange(isInside, {})); |
| | | }); |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | mapCtx.moveToLocation({ |
| | | latitude: lat, |
| | | longitude: lng, |
| | | // mapCtx.moveToLocation({ |
| | | // latitude: lat, |
| | | // longitude: lng, |
| | | // }); |
| | | const points = extractPointsFromPolygons(newPolygons); |
| | | mapCtx.includePoints({ |
| | | points: points, |
| | | padding: [100, 100, 100, 100] |
| | | }); |
| | | } else if (!hasCachedSite) { |
| | | getLocationInfo(); |
| | |
| | | } else if (!hasCachedSite) { |
| | | getLocationInfo(); |
| | | } |
| | | }, []); |
| | | |
| | | useEffect(() => { |
| | | let cancelled = false; |
| | | const run = async () => { |
| | | const local = await preloadImageToTemp(tooltipBgUrl); |
| | | if (!cancelled) setTooltipBgSrc(local); |
| | | }; |
| | | run(); |
| | | return () => { cancelled = true; }; |
| | | }, []); |
| | | |
| | | // 更新地图数据 |
| | |
| | | }) |
| | | } |
| | | |
| | | const confirmSite = () => { |
| | | const confirmSite = async () => { |
| | | if (!selectedSite.name) { |
| | | Taro.showToast({ |
| | | title: '请选择站点', |
| | |
| | | }) |
| | | } |
| | | if (pageType === 'end') { |
| | | const otp = await checkOtp(false); |
| | | if (!otp) { |
| | | jumpTo({ url: '/pages/user/login?hideBack=1' }); |
| | | return |
| | | } |
| | | const startSiteCache = Taro.getStorageSync('startSite'); |
| | | if (startSiteCache?.electronicAreaId && selectedSite?.electronicAreaId && startSiteCache.electronicAreaId != selectedSite.electronicAreaId) { |
| | | Taro.showToast({ |
| | |
| | | // 关闭提示弹窗 |
| | | const handleCloseTipPopup = () => { |
| | | setShowTipPopup(false); |
| | | setTipType(''); |
| | | setTimeout(() => { |
| | | setTipType(''); |
| | | }, 600); |
| | | } |
| | | |
| | | // 切换子站点 |
| | |
| | | onRegionChange={handleRegionChange} |
| | | style={{ width: '100%', height: '100%' }} |
| | | > |
| | | {/* 选择城市 */} |
| | | {/* 返回 */} |
| | | <View onClick={toBack} className="select-city"> |
| | | <Image className="select-city-icon" src={backIcon} /> |
| | | </View> |
| | |
| | | {((showToolTip && selectedSite.name) || outOfServiceRange) && ( |
| | | <View className="toolTip"> |
| | | {(outOfServiceRange || selectedSite.time) && ( |
| | | <View className="toolTip-bg"> |
| | | <View className="toolTip-bg" style={{ backgroundImage: `url(${tooltipBgSrc})` }}> |
| | | <Text className="toolTip-bg-text">{outOfServiceRange ? `不在运营区` : `限停${selectedSite.time}分钟`}</Text> |
| | | </View> |
| | | )} |
| | |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 10px; |
| | | |
| | | .cancel-icon { |
| | | width: 32px; |
| | | height: 32px; |
| | | width: 36px; |
| | | height: 36px; |
| | | margin: 0 24px; |
| | | } |
| | | } |
| | | } |
| | |
| | | import address from '../../assets/address.png' |
| | | import arrow from '../../assets/arrow.png' |
| | | import { getStartPoints, getEndPoints, queryPointByName } from '../../api/site-search'; |
| | | import { formatDistance, debounce } from '../../../utils/Robotaxi-public-method'; |
| | | import { formatDistance, debounce, getDistance } from '../../../utils/Robotaxi-public-method'; |
| | | import { getOrderDistance } from '../../api/select-map-site'; |
| | | import { AMAP_KEY } from '../../../constants/api'; |
| | | import amapFile from '../../../libs/amap-wx.130.js'; |
| | | import { checkOtp } from '../../../utils/tools'; |
| | | import TipPopup from '../select-map-site/components/tip-popup'; |
| | | import { getNearestSite } from '../../api/home'; |
| | | |
| | | |
| | | |
| | |
| | | const [pageType, setPageType] = useState('') //出发地 start 目的地 end |
| | | const [showAllHistory, setShowAllHistory] = useState(false) // 是否展示全部历史记录 |
| | | const [showAllRecommend, setShowAllRecommend] = useState(false) // 是否展示全部推荐站点 |
| | | const [distanceConfig, setDistanceConfig] = useState<any>({}) |
| | | const [showTipPopup, setShowTipPopup] = useState(false) |
| | | const [tipType, setTipType] = useState('') |
| | | |
| | | const MAX_SHOW_COUNT = 5 // 默认最多显示5条 |
| | | const MAX_EXPANDED_COUNT = 10 |
| | | |
| | | const router = Taro.useRouter(); |
| | | |
| | | const fetchNearbySites = async (lat: number, lon: number, electronicAreaId?: string | number) => { |
| | | const fetchNearbySites = async (lat: number, lon: number) => { |
| | | const selectedCity = Taro.getStorageSync('selectedCity'); |
| | | const payload = { |
| | | cityCode: selectedCity?.code || '', |
| | | electronicAreaId: electronicAreaId ? Number(electronicAreaId) : 0, |
| | | lat: Number(lat), |
| | | lon: Number(lon) |
| | | }; |
| | | |
| | | try { |
| | | const res = await queryPointByName(payload); |
| | | const { availablePoints } = res; |
| | | if (Array.isArray(availablePoints)) { |
| | | const list = availablePoints.map(item => ({ |
| | | const res = await getNearestSite({ |
| | | cityCode: selectedCity?.code || '', |
| | | latitude: Number(lat), |
| | | longitude: Number(lon), |
| | | }); |
| | | const points = Array.isArray(res?.points) ? res.points : []; |
| | | if (points.length > 0) { |
| | | const list = points.map(item => ({ |
| | | ...item, |
| | | distance: item.distance |
| | | })); |
| | |
| | | checkOtp(false).then(otp => { |
| | | if (otp) { |
| | | if (lat && lon) { |
| | | fetchNearbySites(Number(lat), Number(lon), electronicAreaId); |
| | | fetchNearbySites(Number(lat), Number(lon)); |
| | | const payload = { |
| | | lat: Number(lat), |
| | | lon: Number(lon) |
| | |
| | | } |
| | | }, []) |
| | | |
| | | useEffect(() => { |
| | | getOrderDistance().then((res: any) => { |
| | | if (res) { |
| | | setDistanceConfig({ |
| | | maxDistance: res.maxDistance, |
| | | minDistance: res.minDistance, |
| | | adsorptionDistance: res.adsorptionDistance |
| | | }); |
| | | } |
| | | }).catch(() => { }); |
| | | }, []); |
| | | |
| | | const searchSite = async (val?: string) => { |
| | | setNonOperationalSite([]); |
| | | const queryVal = val !== undefined ? val : searchValue; |
| | |
| | | ...item, |
| | | distance: item.distance |
| | | })); |
| | | setSiteList(list); |
| | | setSiteList(list.slice(0, MAX_EXPANDED_COUNT)); |
| | | } else { |
| | | setSiteList([]); |
| | | } |
| | |
| | | ...item, |
| | | longitude: Number(locationArr[0]), |
| | | latitude: Number(locationArr[1]), |
| | | distance: formatDistance(item.distance) |
| | | // distance: formatDistance(item.distance) |
| | | } |
| | | }); |
| | | setNonOperationalSite(list.slice(0, MAX_SHOW_COUNT)); |
| | |
| | | }) |
| | | } |
| | | |
| | | const onChangeSelectSite = (item, isNonOperational = true) => { |
| | | const onChangeSelectSite = async (item, isNonOperational = true) => { |
| | | if (!isNonOperational) { |
| | | Taro.showToast({ |
| | | title: '当前站点不在运营区域内', |
| | | title: '当前点位暂未开放', |
| | | icon: 'none', |
| | | }) |
| | | return |
| | |
| | | }) |
| | | return |
| | | } |
| | | try { |
| | | if (!distanceConfig?.maxDistance) { |
| | | const res = await getOrderDistance(); |
| | | if (res) { |
| | | setDistanceConfig({ |
| | | maxDistance: res.maxDistance, |
| | | minDistance: res.minDistance, |
| | | adsorptionDistance: res.adsorptionDistance |
| | | }); |
| | | } |
| | | } |
| | | const loc = await Taro.getLocation({ type: 'gcj02' }); |
| | | const distance = getDistance(loc.latitude, loc.longitude, item.latitude, item.longitude); |
| | | if (distanceConfig?.maxDistance && distance > distanceConfig.maxDistance) { |
| | | setTipType('2'); |
| | | setShowTipPopup(true); |
| | | return; |
| | | } |
| | | } catch { } |
| | | Taro.setStorageSync('startSite', item); |
| | | Taro.navigateBack() |
| | | } else if (pageType == 'end') { |
| | | const otp = await checkOtp(false); |
| | | if (!otp) { |
| | | Taro.navigateTo({ url: '/pages/user/login?hideBack=1' }); |
| | | return |
| | | } |
| | | const startSiteCache = Taro.getStorageSync('startSite'); |
| | | if (startSiteCache?.electronicAreaId && item?.electronicAreaId && startSiteCache.electronicAreaId !== item.electronicAreaId) { |
| | | Taro.showToast({ |
| | |
| | | }) |
| | | return |
| | | } |
| | | try { |
| | | if (!distanceConfig?.minDistance) { |
| | | const res = await getOrderDistance(); |
| | | if (res) { |
| | | setDistanceConfig({ |
| | | maxDistance: res.maxDistance, |
| | | minDistance: res.minDistance, |
| | | adsorptionDistance: res.adsorptionDistance |
| | | }); |
| | | } |
| | | } |
| | | if (startSiteCache && startSiteCache.longitude && startSiteCache.latitude) { |
| | | const distance = getDistance(startSiteCache.latitude, startSiteCache.longitude, item.latitude, item.longitude); |
| | | if (distanceConfig?.minDistance && distance < distanceConfig.minDistance) { |
| | | setTipType('1'); |
| | | setShowTipPopup(true); |
| | | return; |
| | | } |
| | | } |
| | | } catch { } |
| | | Taro.setStorageSync('endSite', item) |
| | | Taro.reLaunch({ |
| | | url: '/robotaxi/pages/begin-route/index', |
| | |
| | | setSearchValue(''); |
| | | searchSite(''); |
| | | } |
| | | const handleCloseTipPopup = () => { |
| | | setShowTipPopup(false); |
| | | setTipType(''); |
| | | } |
| | | |
| | | |
| | | const recommendList = siteList.length > 0 ? siteList : nearbySiteList; |
| | | const visibleHistory = showAllHistory ? historySite : historySite.slice(0, MAX_SHOW_COUNT); |
| | | const visibleHistory = showAllHistory ? historySite.slice(0, MAX_EXPANDED_COUNT) : historySite.slice(0, MAX_SHOW_COUNT); |
| | | const visibleRecommend = showAllRecommend ? recommendList : recommendList.slice(0, MAX_SHOW_COUNT); |
| | | |
| | | // 使用数据截断展示,不再通过高度裁剪内容 |
| | |
| | | { |
| | | searchValue && |
| | | <View onClick={clearSearchValue} className='cancel-icon-box'> |
| | | <Image className='cancel-icon' src={require('../../assets/close-icon.png')} /> |
| | | <Image className='cancel-icon' src={require('../../assets/clear-icon.png')} /> |
| | | </View> |
| | | } |
| | | </View> |
| | |
| | | </View> |
| | | {/* 历史记录 */} |
| | | { |
| | | historySite.length > 0 && |
| | | (historySite.length > 0 && !searchValue) && |
| | | <View className='history-card'> |
| | | <View className='card-header'>历史记录</View> |
| | | <View className='card-content list-content' id='history-card-content'> |
| | |
| | | <View className='history-name'>{item.name || '_'}</View> |
| | | <View className='history-address-box'> |
| | | <View className='history-address'>{item.address || '-'}</View> |
| | | <View className='history-distance'>{item.distance}</View> |
| | | {/* <View className='history-distance'>{item.distance}</View> */} |
| | | </View> |
| | | </View> |
| | | </View> |
| | |
| | | </View> |
| | | } |
| | | { |
| | | (!siteList.length && !nearbySiteList.length && !nonOperationalSite.length && !historySite.length) && |
| | | (!siteList.length && !nearbySiteList.length && !nonOperationalSite.length && (!searchValue ? !historySite.length : true)) && |
| | | <View className='no-data-box'> |
| | | <Image className='no-data-image' src={require('../../assets/no-data-city.png')} /> |
| | | <Image className='no-data-image' src={require('../../assets/no-data-site.png')} /> |
| | | <View>暂无站点</View> |
| | | </View> |
| | | } |
| | | <TipPopup |
| | | show={showTipPopup} |
| | | onClose={handleCloseTipPopup} |
| | | tipType={tipType} |
| | | /> |
| | | </View> |
| | | ) |
| | | } |
| | |
| | | } |
| | | |
| | | // 获取地图数据(围栏检查+附近站点) |
| | | export const fetchMapData = async (lat: number, lng: number) => { |
| | | export const fetchMapData = async (lat: number, lng: number, cityCode?: string) => { |
| | | try { |
| | | // 1. 先检查是否在运营区域内 |
| | | const fenceRes: any = await getFenceCheck({ latitude: lat, longitude: lng }).catch(e => { |
| | | const fenceRes: any = await getFenceCheck({ latitude: lat, longitude: lng, cityCode }).catch(e => { |
| | | return { status: 'inside' }; // 默认兜底 |
| | | }); |
| | | |
| | |
| | | city: cityName, |
| | | success: (data) => { |
| | | if (data && data.tips && data.tips.length > 0) { |
| | | // 优先取第一个有坐标的结果 |
| | | const tip = data.tips.find(t => t.location && t.location.length > 0); |
| | | if (tip) { |
| | | const locationArr = tip.location.split(','); |
| | | if (locationArr.length === 2) { |
| | | resolve({ |
| | | longitude: Number(locationArr[0]), |
| | | latitude: Number(locationArr[1]) |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | reject('No valid location found'); |
| | | // 优先取第一个有坐标的结果 |
| | | const tip = data.tips.find(t => t.location && t.location.length > 0); |
| | | if (tip) { |
| | | const locationArr = tip.location.split(','); |
| | | if (locationArr.length === 2) { |
| | | resolve({ |
| | | longitude: Number(locationArr[0]), |
| | | latitude: Number(locationArr[1]) |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | reject('No valid location found'); |
| | | } else { |
| | | reject('No data found'); |
| | | } |