style(robotaxi): 调整样式细节并优化代码逻辑
- 统一多个组件的圆角大小和边框样式
- 优化优惠券页面布局和状态显示
- 移除未使用的IP定位工具函数
- 简化API响应处理逻辑
- 修复文本溢出和依赖项问题
| | |
| | | |
| | | <View className='privacy-text'> |
| | | <Text className='text-content'> |
| | | 欢迎使用小鹏Robotaxi App!为了更好地保障您的个人权益,我们将通过 《用户服务协议》、《隐私政策》、以及《关于路Robotaxi平台向第三方提供个人信息情况的说明》、《关于关于路Robotaxi平台向第三方提供个人信息情况的说明》等文档来保护您的个人信息。 |
| | | 欢迎使用小鹏Robotaxi App!为了更好地保障您的个人权益,我们将通过 《用户服务协议》、《隐私政策》、以及《关于路Robotaxi平台向第三方提供个人信息情况的说明》、《关于关于路Robotaxi平台向第三方提供个人信息情况的说明》等文档来保护您的个人信息。 |
| | | </Text> |
| | | <View className='txt-tip'>我已满十四周岁,阅读并同意:</View> |
| | | </View> |
| | |
| | | background-color: transparent; |
| | | overflow: hidden; |
| | | margin-top: -52px; |
| | | border-radius: 32px 32px 0 0; |
| | | border-radius: 38px 38px 0 0; |
| | | |
| | | .select-site { |
| | | position: relative; |
| | |
| | | line-height: 44px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | overflow: hidden; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 1; |
| | | /* 设置最大显示行数 */ |
| | | -webkit-box-orient: vertical; |
| | | text-overflow: ellipsis; |
| | | word-break: break-all; |
| | | |
| | | .now-site-address-text { |
| | | margin: 0 2px; |
| | |
| | | |
| | | .activity-item { |
| | | margin-bottom: 24px; |
| | | border-top: 2px solid #fff; |
| | | border: 2px solid #fff; |
| | | border-radius: 32px; |
| | | |
| | | .activity-img { |
| | | border-radius: 32px; |
| | |
| | | // 监听 Banner 列表变化,重新计算高度 |
| | | useEffect(() => { |
| | | calculateBoxHeight(); |
| | | }, [bannerList]); |
| | | }, [bannerList, tripStatus]); |
| | | |
| | | // 监听城市变化,更新电子围栏 |
| | | useEffect(() => { |
| | |
| | | { |
| | | [4, 5, 6, 7].includes(tripStatus) && |
| | | <View className="order-status-left"> |
| | | <Image className="order-status-left-icon" src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e76e8fd6-1f52-482f-b9c0-6a026f4d2797.png' /> |
| | | <Image className="order-status-left-icon" src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/552a3a31-e4dd-4a28-8748-fd8b87dd3055.png' /> |
| | | <View className="order-status-left-detail"> |
| | | <View>{orderData.plateNumber}</View> |
| | | <View>{ORDER_STATUS[tripStatus]}</View> |
| | |
| | | } |
| | | |
| | | // 优惠券列表 |
| | | export const getCouponList = async (payload: GetCouponListPayload): Promise<GetCouponListResponse> => { |
| | | const res = await request({ |
| | | export const getCouponList = (payload: GetCouponListPayload): Promise<GetCouponListResponse> => { |
| | | return request({ |
| | | url: _ROBOTAXI_HOST + '/account/api/v1/mini-program/coupon/list', |
| | | method: 'POST', |
| | | payload |
| | | }); |
| | | |
| | | // 手动转换下划线字段为驼峰 |
| | | const list = (res.list || []).map((item: any) => ({ |
| | | couponId: item.coupon_id, |
| | | title: item.title, |
| | | discountAmount: item.discount_amount, |
| | | expireTime: item.expire_time, |
| | | status: item.status, |
| | | })); |
| | | |
| | | return { |
| | | total: res.total || 0, |
| | | list |
| | | }; |
| | | } |
| | | |
| | | // 领取优惠券 |
| | |
| | | } |
| | | |
| | | // 优惠券详情 |
| | | export const getCouponDetail = async (couponId: string): Promise<CouponDetail> => { |
| | | const res = await request({ |
| | | export const getCouponDetail = (couponId: string): Promise<CouponDetail> => { |
| | | return request({ |
| | | url: _ROBOTAXI_HOST + `/account/api/v1/mini-program/coupon/detail/${couponId}`, |
| | | method: 'POST' |
| | | method: 'GET' |
| | | }); |
| | | |
| | | // 手动转换下划线字段为驼峰 |
| | | return { |
| | | couponId: res.coupon_id, |
| | | title: res.title, |
| | | expireTime: res.expire_time, |
| | | validTimeRange: res.valid_time_range, |
| | | applicableCities: res.applicable_cities || [], |
| | | usageInstructions: res.usage_instructions |
| | | }; |
| | | } |
| | |
| | | } |
| | | const targetOrderId = orderId || Taro.getStorageSync('orderId'); |
| | | if (targetOrderId) { |
| | | // 暂时不调用车辆温度 // 2-12 新增 |
| | | // 暂时不调用车辆温度 |
| | | setTempStatus(19); |
| | | lastConfirmedTempRef.current = 19; |
| | | // queryTemperatureByOrderId({ orderId: targetOrderId }).then((res: any) => { |
| | |
| | | // lastConfirmedTempRef.current = currentTemperature; |
| | | // } |
| | | // }).catch(() => { }); |
| | | |
| | | // 2-12 新增 |
| | | } |
| | | } else if (tripStatus === 5) { |
| | | // 已到达上车点:根据限停时间启动倒计时并处理超时 |
| | | // 2-12 新增 |
| | | const duration = Number(Taro.getStorageSync('orderRouteDuration')); |
| | | if (!Number.isNaN(duration)) { |
| | | setRouteDuration(duration); |
| | | } |
| | | // 2-12 新增 |
| | | const s = Taro.getStorageSync('startSite'); |
| | | const time = Number(s?.time); |
| | | if (!Number.isNaN(time) && time > 0) { |
| | |
| | | 车身编码: {vehicleInfo?.vehicleCode} |
| | | </View> |
| | | </View> |
| | | <Image className="car-card-detail-img" src={vehicleInfo?.vehiclePictures?.[0].url || 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/825d7fa2-6cf6-4a31-aaac-3a4de806e9a3.png'} /> |
| | | <Image className="car-card-detail-img" src={vehicleInfo?.vehiclePictures?.[0].url || 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/552a3a31-e4dd-4a28-8748-fd8b87dd3055.png'} /> |
| | | </View> |
| | | { |
| | | tripStatus == 4 && <View> |
| | |
| | | { |
| | | tripStatus == 5 && |
| | | <View className="car-card-arrive"> |
| | | <View className="car-card-arrive-item" onClick={() => handleUnlock('trunk', () => setTrunkStatus((prev) => !prev))}> |
| | | { |
| | | trunkStatus ? |
| | | <Image className="car-card-arrive-item-img" src={require('../../../assets/trunk-open.png')} /> |
| | | : |
| | | <Image className="car-card-arrive-item-img" src={require('../../../assets/trunk-close.png')} /> |
| | | } |
| | | <View className="car-card-arrive-item-text">{trunkStatus ? '关闭后备箱' : '打开后备箱'}</View> |
| | | <View className="car-card-arrive-item" onClick={() => handleUnlock('trunk', () => setTrunkStatus(true))}> |
| | | <Image className="car-card-arrive-item-img" src={require('../../../assets/trunk-close.png')} /> |
| | | <View className="car-card-arrive-item-text">打开后备箱</View> |
| | | </View> |
| | | <View className="car-card-arrive-item" onClick={() => handleUnlock('door', () => setLockStatus((prev) => !prev))}> |
| | | { |
| | | lockStatus ? |
| | | <Image className="car-card-arrive-item-img" src={require('../../../assets/lock-open.png')} /> |
| | | : |
| | | <Image className="car-card-arrive-item-img" src={require('../../../assets/lock-close.png')} /> |
| | | } |
| | | <View className="car-card-arrive-item-text">{lockStatus ? '加锁车门' : '解锁车门'}</View> |
| | | <View className="car-card-arrive-item" onClick={() => handleUnlock('door', () => setLockStatus(true))}> |
| | | <Image className="car-card-arrive-item-img" src={require('../../../assets/lock-close.png')} /> |
| | | <View className="car-card-arrive-item-text">解锁车门</View> |
| | | </View> |
| | | </View> |
| | | } |
| | |
| | | .calling-car { |
| | | position: relative; |
| | | padding: 40px 42px 132px 34px; |
| | | padding: 40px 42px 88px 34px; |
| | | background: #F2F3F7; |
| | | border-top: 2px solid #fff; |
| | | border-radius: 32px 32px 0 0; |
| | | border-radius: 38px 38px 0 0; |
| | | |
| | | &-bg { |
| | | position: absolute; |
| | |
| | | .select-site { |
| | | position: relative; |
| | | padding: 32px; |
| | | padding-bottom: 56px; |
| | | padding-top: 40px; |
| | | background: #F2F3F7; |
| | | border-top: 2px solid #fff; |
| | | border-radius: 32px 32px 0 0; |
| | | border-radius: 38px 38px 0 0; |
| | | |
| | | .site-top-bg { |
| | | position: absolute; |
| | |
| | | if (routePoints.length > 0) { |
| | | lines.push({ |
| | | points: routePoints, |
| | | color: '#6D7FD9', |
| | | color: '#7B91FF', |
| | | width: 10, |
| | | arrowLine: true, |
| | | }); |
| | |
| | | if (walkingPoints.length > 0) { |
| | | lines.push({ |
| | | points: walkingPoints, |
| | | color: '#6D7FD9', |
| | | color: '#7B91FF', |
| | | width: 6, |
| | | dottedLine: true, |
| | | arrowLine: true, |
| | |
| | | const { orderId } = await saveOrder(payload); |
| | | Taro.setStorageSync('orderId', orderId); |
| | | } catch (error) { |
| | | Taro.showToast({ |
| | | title: '下单失败,请重试', |
| | | icon: 'none', |
| | | }) |
| | | return |
| | | } |
| | | setTripStatus(2); |
| | |
| | | /* Firefox */ |
| | | |
| | | .coupon-item { |
| | | position: relative; |
| | | width: 686px; |
| | | height: 204px; |
| | | z-index: 0; |
| | | border-radius: 24px; |
| | | background: #FFFFFF; |
| | | padding: 32px; |
| | | padding-right: 28px; |
| | | margin-bottom: 24px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | |
| | | .coupon-content { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .coupon-price { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-shrink: 0; |
| | | margin-right: 48px; |
| | | } |
| | | |
| | | .price { |
| | |
| | | letter-spacing: normal; |
| | | /* 功能色/提示色/提示色1 */ |
| | | color: #F58900; |
| | | margin: 0px 48px 0px 2px; |
| | | margin-left: 2px; |
| | | } |
| | | |
| | | .coupon-info { |
| | |
| | | } |
| | | |
| | | .receive { |
| | | flex-shrink: 0; |
| | | width: 104px; |
| | | height: 56px; |
| | | /* 自动布局 */ |
| | |
| | | padding: 25px 0px; |
| | | border-radius: 110px; |
| | | background: #F58900; |
| | | margin-left: 44px; |
| | | |
| | | /* 辅助文字/辅助文字 常规 12 */ |
| | | font-family: PingFang SC; |
| | |
| | | /* 文字色/白色/白色 100% */ |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | .status-img { |
| | | width: 136px; |
| | | height: 140px; |
| | | position: absolute; |
| | | bottom: 0; |
| | | right: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | const toHome = () => { |
| | | Taro.switchTab({ |
| | | url: '/robotaxi/pages/home/index' |
| | | }) |
| | | } |
| | | |
| | | return ( |
| | | <View className='coupon-page'> |
| | | <View className='tab-card'> |
| | |
| | | couponList.length > 0 && |
| | | <View className='coupon-card'> |
| | | {couponList.map((item, index) => { |
| | | return <View className='coupon-item' key={item.couponId || index}> |
| | | |
| | | <View className='coupon-price'> |
| | | <Text className='price'>{item.discountAmount}</Text> |
| | | <Text className='unit'>元</Text> |
| | | </View> |
| | | <View className='coupon-info'> |
| | | <Text className='name'>{item.title}</Text> |
| | | <Text className='time'>{item.expireTime}到期</Text> |
| | | <View className='more' onClick={() => openPopup(item)}> |
| | | <Text className='more-detail'>查看详情</Text> |
| | | <Image className='more-img' src={icon}></Image> |
| | | return <View className='coupon-item' key={item.couponId || index} style={{ opacity: ![0, 1].includes(item.status) ? 0.5 : 1 }}> |
| | | <View className='coupon-content'> |
| | | <View className='coupon-price'> |
| | | <Text className='price'>{item.discountAmount}</Text> |
| | | <Text className='unit'>元</Text> |
| | | </View> |
| | | <View className='coupon-info'> |
| | | <Text className='name'>{item.title}</Text> |
| | | <Text className='time'>{item.expireTime}到期</Text> |
| | | <View className='more' onClick={() => openPopup(item)}> |
| | | <Text className='more-detail'>查看详情</Text> |
| | | <Image className='more-img' src={icon}></Image> |
| | | </View> |
| | | </View> |
| | | </View> |
| | | {/* 状态 1-未使用 2-已使用 3-已过期 0待领取 */} |
| | | {item.status === 0 ? ( |
| | | <View className='receive' onClick={(e) => handleReceive(e, item)}>领取</View> |
| | | ) : item.status === 1 ? ( |
| | | <View className='receive' style={{ backgroundColor: '#ccc' }}>未使用</View> |
| | | <View onClick={toHome} className='receive' style={{ backgroundColor: '#F58900' }}>去使用</View> |
| | | ) : item.status === 2 ? ( |
| | | <View className='receive' style={{ backgroundColor: '#f5f5f5', color: '#999' }}>已使用</View> |
| | | <Image className='status-img' src={require('../../assets/coupon-used.png')} /> |
| | | ) : ( |
| | | <View className='receive' style={{ backgroundColor: '#f5f5f5', color: '#999' }}>已过期</View> |
| | | <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.validTimeRange}</Text> |
| | | <Text className='remark-value'>{currentCoupon.timeRule}</Text> |
| | | </View> |
| | | <View className='remark-card'> |
| | | <Text className='remark-label'>使用地区</Text> |
| | |
| | | .complaint-text { |
| | | padding: 24px 0px; |
| | | box-sizing: border-box; |
| | | border: 1.4px solid #000000; |
| | | border: 1px solid #000000; |
| | | width: 548px; |
| | | height: 92px; |
| | | border-radius: 16px; |
| | |
| | | |
| | | export default function OrderPage() { |
| | | const router = useRouter(); |
| | | const { orderId } = router.params; |
| | | const [showAllFaq, setShowAllFaq] = useState(false); // 是否显示全部FAQ |
| | | const [expandedItems, setExpandedItems] = useState<number[]>([]); // 展开的FAQ项ID列表 |
| | | const [fareData, setFareData] = useState<FareBreakdownResponse | null>(null); |
| | |
| | | const [showEvaluatePopup, setShowEvaluatePopup] = useState(false); |
| | | |
| | | useEffect(() => { |
| | | if (orderId) { |
| | | fetchData(orderId); |
| | | } |
| | | fetchData(router.params?.orderId); |
| | | fetchFAQ(); |
| | | }, [orderId]); |
| | | }, []); |
| | | |
| | | const fetchData = async (id: string) => { |
| | | try { |
| | |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 通过 IP 获取当前位置信息(用于定位授权失败时的兜底) |
| | | */ |
| | | export const getLocationByIP = (): Promise<any> => { |
| | | return new Promise(async (resolve, reject) => { |
| | | try { |
| | | // 1. 获取当前 network IP |
| | | // 微信小程序没有直接获取公网 IP 的接口,通常由后端从请求头中获取,或者通过第三方接口获取 |
| | | // 这里模拟调用服务端提供的 IP 定位接口,假设接口会自动识别请求来源 IP |
| | | const res: any = await request({ |
| | | url: _ROBOTAXI_HOST + '/map/base/v1/mini-program/point/getPointByIp', |
| | | method: 'GET', |
| | | }); |
| | | |
| | | if (res && res.point && typeof res.point.latitude === 'number' && typeof res.point.longitude === 'number') { |
| | | resolve({ |
| | | longitude: res.point.longitude, |
| | | latitude: res.point.latitude, |
| | | city: res.city || '', |
| | | adcode: res.adcode || '' |
| | | }); |
| | | } else { |
| | | // 如果后端接口不可用或未返回数据,尝试使用第三方接口作为备选获取 IP |
| | | Taro.request({ |
| | | url: 'https://api.ipify.org?format=json', |
| | | success: (ipRes) => { |
| | | const ip = ipRes.data?.ip; |
| | | if (ip) { |
| | | // 拿着获取到的 IP 去请求服务端接口 |
| | | request({ |
| | | url: _ROBOTAXI_HOST + '/map/base/v1/mini-program/point/getPointByIp', |
| | | method: 'POST', |
| | | payload: { ip } |
| | | }).then((businessRes: any) => { |
| | | if (businessRes && businessRes.point && typeof businessRes.point.latitude === 'number' && typeof businessRes.point.longitude === 'number') { |
| | | resolve({ |
| | | longitude: businessRes.point.longitude, |
| | | latitude: businessRes.point.latitude, |
| | | city: businessRes.city || '', |
| | | adcode: businessRes.adcode || '' |
| | | }); |
| | | } else { |
| | | reject('Business IP location failed'); |
| | | } |
| | | }).catch(reject); |
| | | } else { |
| | | reject('Failed to get network IP'); |
| | | } |
| | | }, |
| | | fail: reject |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | reject(error); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 获取城市中心经纬度 |
| | | export const getCityLocation = (cityName: string): Promise<{ latitude: number, longitude: number }> => { |
| | | return new Promise((resolve, reject) => { |