feat(robotaxi): 更新车辆标记图标,优化行程页面交互与样式
| | |
| | | const MOVE_THRESHOLD_METERS = 500; // 触发新搜索的位移阈值(米) |
| | | const ANIMATION_DURATION_MS = 2000; // 车辆位移动画时长(毫秒) |
| | | const VEHICLE_LOCATION_EPSILON = 0.000001; // 车辆经纬度可见变化阈值 |
| | | const VEHICLE_MARKER_ICON = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car.png'; // 车辆 icon 地址 |
| | | const VEHICLE_MARKER_ICON = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car-v2.png'; // 车辆 icon 地址 |
| | | |
| | | /** |
| | | * 归一化角度到 [0, 360) 区间。 |
| | |
| | | color: rgba(0, 0, 0, 0.9); |
| | | } |
| | | |
| | | &-fare-original { |
| | | margin-left: 26px; |
| | | font-size: 20px; |
| | | font-weight: normal; |
| | | line-height: 20px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.4); |
| | | } |
| | | |
| | | &-tip-icon { |
| | | width: 22px; |
| | | height: 22px; |
| | |
| | | return '修改次数获取失败'; |
| | | }; |
| | | |
| | | /** |
| | | * 置灰确认按钮提示文案。 |
| | | */ |
| | | const MODIFY_DESTINATION_DISABLED_TOAST = '车辆临近终点,不支持修改目的地'; |
| | | |
| | | export default function ModifyDestinationReview({ |
| | | orderId = '', |
| | | startSite, |
| | |
| | | const isModifyCountDisabled = remainingModifyCount === MODIFY_DESTINATION_DISABLED_COUNT; // 后端明确返回无修改机会 |
| | | const isConfirmDisabled = isConfirmingModifyDestination || isLoadingModifyCount || !isModifyCountReady || isRouteDataIncomplete || isModifyCountDisabled; // 提交中、次数未就绪、路线未就绪或无次数时禁用确认 |
| | | const modifyDestinationTipText = getModifyDestinationTipText(remainingModifyCount, isLoadingModifyCount); // 修改次数展示文案 |
| | | const previousEstimatedFareText = previousFareEstimate?.finalFareStr || '-'; // 改终点前上一版预估价 |
| | | |
| | | /** |
| | | * 置灰态点击确认按钮时给出明确提示。 |
| | | */ |
| | | const handleDisabledConfirmToast = () => { |
| | | Taro.showToast({ |
| | | title: MODIFY_DESTINATION_DISABLED_TOAST, |
| | | icon: 'none' |
| | | }); |
| | | }; |
| | | |
| | | const isDragging = useRef(false); |
| | | const startY = useRef(0); |
| | |
| | | {isOperateFree ? ( |
| | | <View className='modify-destination-review-operate-free'>载客测试免费</View> |
| | | ) : ( |
| | | <View className='modify-destination-review-fare-main' onClick={toPriceDescription}> |
| | | <Image className='modify-destination-review-tip-icon' src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/tip-default-icon.png" /> |
| | | 预估 |
| | | <Text className='modify-destination-review-fare-num'>{fareEstimate?.finalFareStr || '-'}</Text> |
| | | 元 |
| | | <View> |
| | | <View className='modify-destination-review-fare-main' onClick={toPriceDescription}> |
| | | <Image className='modify-destination-review-tip-icon' src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/tip-default-icon.png" /> |
| | | 预估 |
| | | <Text className='modify-destination-review-fare-num'>{fareEstimate?.finalFareStr || '-'}</Text> |
| | | 元 |
| | | </View> |
| | | <View className='modify-destination-review-fare-original'>原价格:{previousEstimatedFareText}元</View> |
| | | </View> |
| | | )} |
| | | {isOperateFree ? null : fareEstimate.discountAmount > 0 && |
| | |
| | | <View |
| | | className={`modify-destination-review-confirm${isConfirmDisabled ? ' modify-destination-review-confirm-disabled' : ''}`} |
| | | onClick={() => { |
| | | if (isConfirmDisabled) return; |
| | | if (isConfirmDisabled) { |
| | | handleDisabledConfirmToast(); |
| | | return; |
| | | } |
| | | onConfirm(); |
| | | }} |
| | | > |
| | |
| | | ? '路线加载中...' |
| | | : '确认修改'} |
| | | </View> |
| | | {isConfirmDisabled && <View className='modify-destination-review-confirm-mask' />} |
| | | {isConfirmDisabled && <View className='modify-destination-review-confirm-mask' onClick={handleDisabledConfirmToast} />} |
| | | </View> |
| | | </View> |
| | | </View> |
| | |
| | | const LOCATION_REFRESH_INTERVAL_MS = 5000; |
| | | const MAP_INCLUDE_POINTS_PADDING = [32, 32, 120, 32]; |
| | | const USER_MARKER_ICON_URL = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/site-poi.png'; |
| | | const VEHICLE_MARKER_ICON_URL = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car.png'; |
| | | const VEHICLE_MARKER_ICON_URL = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car-v2.png'; |
| | | |
| | | type LocationPoint = { |
| | | latitude: number; |
| | |
| | | id: deps.vehicleMarkerId, |
| | | latitude: deps.vehicleLocation.latitude, |
| | | longitude: deps.vehicleLocation.longitude, |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car.png', |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car-v2.png', |
| | | width: 20, |
| | | height: 40, |
| | | anchor: { x: 0.5, y: 0.5 }, |
| | |
| | | id: ROUTE_SUMMARY_MARKER_ID, |
| | | latitude: summaryLocation.latitude, |
| | | longitude: summaryLocation.longitude, |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car.png', |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car-v2.png', |
| | | width: 1, |
| | | height: 1, |
| | | alpha: 0, |
| | |
| | | </Map> |
| | | <View className='container'> |
| | | <View |
| | | className={`share-contact ${showReminder ? 'has-tip' : ''}`} |
| | | className={`map-entry ${showReminder ? 'has-tip' : ''}`} |
| | | onClick={handleContactService} |
| | | > |
| | | <Image className='share-contact-icon' src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/contact-service.png' /> |
| | | <Image className='map-entry-icon' src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/contact-service.png' /> |
| | | </View> |
| | | <View className='container-inner'> |
| | | <View catchMove onClick={(event) => event.stopPropagation()}> |
| | |
| | | id: deps.vehicleMarkerId, |
| | | latitude: deps.vehicleLocation.latitude, |
| | | longitude: deps.vehicleLocation.longitude, |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car.png', |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car-v2.png', |
| | | width: 20, |
| | | height: 40, |
| | | anchor: { x: 0.5, y: 0.5 }, |
| | |
| | | } |
| | | } |
| | | |
| | | .share-contact { |
| | | position: absolute; |
| | | bottom: calc(100% + 16px); |
| | | .map-entry { |
| | | position: fixed; |
| | | top: 204px; |
| | | right: 24px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 72px; |
| | | height: 72px; |
| | | border-radius: 16px; |
| | | background: #ffffff; |
| | | box-shadow: 0px 4px 16px 0px rgba(84, 92, 143, 0.1); |
| | | z-index: 11; |
| | | transition: bottom 0.3s ease; /* 可选,平滑过渡效果 */ |
| | | background: rgba(255, 255, 255, 1); |
| | | overflow: hidden; |
| | | padding: 4px 4px; |
| | | z-index: 300; |
| | | |
| | | &.has-tip { |
| | | bottom: calc(100% + 16px); |
| | | top: 204px; |
| | | } |
| | | |
| | | .share-contact-icon { |
| | | /* 自动布局子元素 */ |
| | | .map-entry-icon { |
| | | width: 64px; |
| | | height: 64px; |
| | | z-index: 12; |
| | | } |
| | | } |
| | | |
| | |
| | | font-size: 32px; |
| | | font-weight: 500; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | |
| | | .calling-car-head-title { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .calling-car-head-title-eta { |
| | | font-size: 40px; |
| | | font-weight: 500; |
| | |
| | | border-radius: 40px 40px 0 0; |
| | | padding: 14px 32px; |
| | | box-sizing: border-box; |
| | | background: linear-gradient( |
| | | 96deg, |
| | | #71c5ff -10%, |
| | | #87ceff -10%, |
| | | #898afa 103%, |
| | | #8283ff 103% |
| | | ); |
| | | background: linear-gradient(96deg, |
| | | #71c5ff -10%, |
| | | #87ceff -10%, |
| | | #898afa 103%, |
| | | #8283ff 103%); |
| | | z-index: 8; |
| | | |
| | | .tip-box-content { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .tip-box-text { |
| | | font-size: 24px; |
| | | line-height: 40px; |
| | | height: 40px; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .tip-box-btn { |
| | | width: 120px; |
| | | height: 48px; |
| | |
| | | &.start { |
| | | background: #768aff; |
| | | } |
| | | |
| | | &.end { |
| | | background: #8bb654; |
| | | } |
| | |
| | | font-size: 24px; |
| | | line-height: 40px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | |
| | | &.disabled-text { |
| | | color: rgba(0, 0, 0, 0.4); |
| | | } |
| | |
| | | } |
| | | |
| | | .custom-callout { |
| | | position: relative; |
| | | z-index: 999; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-direction: column; |
| | | overflow: visible; |
| | | position: relative; |
| | | z-index: 999; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-direction: column; |
| | | overflow: visible; |
| | | } |
| | | |
| | | .pd-6-10 { |
| | | padding: 8px 20px !important; |
| | | padding: 8px 20px !important; |
| | | } |
| | | |
| | | .waiting-callout-content { |
| | | z-index: 2; |
| | | position: absolute; |
| | | top: 240px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | z-index: 2; |
| | | position: absolute; |
| | | top: 240px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | } |
| | | |
| | | .custom-callout-content { |
| | | display: flex; |
| | | align-items: center; |
| | | white-space: nowrap; |
| | | border-radius: 16px; |
| | | background: rgba(255, 255, 255, 1); |
| | | font-size: 24px; |
| | | font-weight: 400; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | line-height: 38px; |
| | | box-shadow: 0px 4px 16px 0px rgba(45, 48, 64, 0.2); |
| | | padding: 8px 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | white-space: nowrap; |
| | | border-radius: 16px; |
| | | background: rgba(255, 255, 255, 1); |
| | | font-size: 24px; |
| | | font-weight: 400; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | line-height: 38px; |
| | | box-shadow: 0px 4px 16px 0px rgba(45, 48, 64, 0.2); |
| | | padding: 8px 20px; |
| | | } |
| | | |
| | | .route-summary-callout { |
| | | position: relative; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | min-width: 188px; |
| | | height: 54px; |
| | | padding-left: 20px; |
| | | padding-right: 20px; |
| | | box-sizing: border-box; |
| | | border-radius: 27px; |
| | | background: #ffffff; |
| | | border: 2px solid rgba(109, 127, 217, 0.18); |
| | | box-shadow: 0px 6px 18px 0px rgba(45, 48, 64, 0.12); |
| | | overflow: visible; |
| | | position: relative; |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | min-width: 188px; |
| | | height: 54px; |
| | | padding-left: 20px; |
| | | padding-right: 20px; |
| | | box-sizing: border-box; |
| | | border-radius: 27px; |
| | | background: #ffffff; |
| | | border: 2px solid rgba(109, 127, 217, 0.18); |
| | | box-shadow: 0px 6px 18px 0px rgba(45, 48, 64, 0.12); |
| | | overflow: visible; |
| | | } |
| | | |
| | | .route-summary-callout-text { |
| | | position: relative; |
| | | z-index: 2; |
| | | font-size: 24px; |
| | | font-weight: 500; |
| | | line-height: 38px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | white-space: nowrap; |
| | | text-align: center; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | position: relative; |
| | | z-index: 2; |
| | | font-size: 24px; |
| | | font-weight: 500; |
| | | line-height: 38px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | white-space: nowrap; |
| | | text-align: center; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .txt-center { |
| | | text-align: center; |
| | | text-align: center; |
| | | } |
| | | |
| | | .car-time { |
| | | font-size: 24px; |
| | | letter-spacing: normal; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 24px; |
| | | letter-spacing: normal; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .car-time-num { |
| | | font-size: 36px; |
| | | letter-spacing: normal; |
| | | color: #6D7FD9; |
| | | margin-bottom: 4px; |
| | | font-size: 36px; |
| | | letter-spacing: normal; |
| | | color: #6D7FD9; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .custom-callout-icon { |
| | | width: 32px; |
| | | height: 32px; |
| | | flex-shrink: 0; |
| | | display: block; |
| | | width: 32px; |
| | | height: 32px; |
| | | flex-shrink: 0; |
| | | display: block; |
| | | } |
| | | |
| | | .car-callout-android { |
| | | z-index: 9; |
| | | pointer-events: none; |
| | | will-change: transform; |
| | | backface-visibility: hidden; |
| | | transition: transform 0.18s linear; |
| | | z-index: 9; |
| | | pointer-events: none; |
| | | will-change: transform; |
| | | backface-visibility: hidden; |
| | | transition: transform 0.18s linear; |
| | | } |
| | | |
| | | .waiting-callout { |
| | | position: relative; |
| | | z-index: 999; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | justify-content: flex-start; |
| | | overflow: visible; // 确保辐射动画不被裁剪 |
| | | min-width: 700px; |
| | | height: 600px; |
| | | position: relative; |
| | | z-index: 999; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | justify-content: flex-start; |
| | | overflow: visible; // 确保辐射动画不被裁剪 |
| | | min-width: 700px; |
| | | height: 600px; |
| | | } |
| | | |
| | | .waiting-radar { |
| | | position: absolute; |
| | | bottom: 50px; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 350px; |
| | | pointer-events: none; |
| | | z-index: 1; |
| | | position: absolute; |
| | | bottom: 50px; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 350px; |
| | | pointer-events: none; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .waiting-radar-img { |
| | | position: fixed; |
| | | width: 700px; |
| | | height: 350px; |
| | | top: 48%; |
| | | left: 50%; |
| | | transform: translate(-50%, -48%); |
| | | position: fixed; |
| | | width: 700px; |
| | | height: 350px; |
| | | top: 48%; |
| | | left: 50%; |
| | | transform: translate(-50%, -48%); |
| | | } |
| | | |
| | | .waiting-callout-bg { |
| | | position: relative; |
| | | height: 70px; |
| | | min-width: 155px; |
| | | text-align: center; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | justify-content: center; |
| | | width: fit-content; |
| | | position: relative; |
| | | height: 70px; |
| | | min-width: 155px; |
| | | text-align: center; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | justify-content: center; |
| | | width: fit-content; |
| | | } |
| | | |
| | | .waiting-callout-bg-img { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | z-index: 1; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .waiting-callout-bg-text { |
| | | position: relative; |
| | | z-index: 100; |
| | | font-size: 22px; |
| | | font-weight: 400; |
| | | letter-spacing: normal; |
| | | color: #FFFFFF; |
| | | line-height: 48px; |
| | | padding: 0 20px; |
| | | box-sizing: border-box; |
| | | white-space: nowrap; |
| | | position: relative; |
| | | z-index: 100; |
| | | font-size: 22px; |
| | | font-weight: 400; |
| | | letter-spacing: normal; |
| | | color: #FFFFFF; |
| | | line-height: 48px; |
| | | padding: 0 20px; |
| | | box-sizing: border-box; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .waiting-callout-txt { |
| | | z-index: 200; |
| | | border-radius: 16px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | font-size: 24px; |
| | | font-weight: 400; |
| | | background: rgba(255, 255, 255, 1); |
| | | padding: 8px 20px; |
| | | line-height: 48px; |
| | | display: flex; |
| | | z-index: 200; |
| | | border-radius: 16px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | font-size: 24px; |
| | | font-weight: 400; |
| | | background: rgba(255, 255, 255, 1); |
| | | padding: 8px 20px; |
| | | line-height: 48px; |
| | | display: flex; |
| | | } |
| | | |
| | | .pr-6 { |
| | | padding-right: 6px; |
| | | padding-right: 6px; |
| | | } |
| | | |
| | | .pr-0 { |
| | | padding-right: 0; |
| | | } |
| | | padding-right: 0; |
| | | } |
| | |
| | | id: ROUTE_SUMMARY_MARKER_ID, |
| | | latitude: midpoint.latitude, |
| | | longitude: midpoint.longitude, |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car.png', |
| | | iconPath: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/marker-car-v2.png', |
| | | width: 1, |
| | | height: 1, |
| | | alpha: 0, |