| | |
| | | .select-city { |
| | | .select-city-container { |
| | | position: absolute; |
| | | top: 104px; |
| | | left: 40px; |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 32px; |
| | | font-weight: 500; |
| | | line-height: 48px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | |
| | | .select-city-icon { |
| | | width: 28px; |
| | | height: 28px; |
| | | margin-left: 4px; |
| | | .select-city { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 32px; |
| | | font-weight: 500; |
| | | line-height: 48px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | |
| | | .select-city-icon { |
| | | width: 28px; |
| | | height: 28px; |
| | | margin-left: 4px; |
| | | } |
| | | } |
| | | |
| | | .weather-info { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 20px; |
| | | font-size: 28px; |
| | | font-weight: 500; |
| | | line-height: 48px; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | |
| | | .weather-icon { |
| | | width: 40px; |
| | | height: 40px; |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | background-clip: text; |
| | | text-fill-color: transparent; |
| | | } |
| | | } |
| | | } |
| | |
| | | import contactService from '../../../robotaxi/assets/contact-service.png' |
| | | import coupon from '../../../robotaxi/assets/coupon.png' |
| | | import suggest from '../../../robotaxi/assets/suggest.png' |
| | | import { getWeatherIconByDesc } from '../../../utils/weather'; |
| | | import originIcon from '../../../robotaxi/assets/origin-icon.png' |
| | | import pointer from '../../../robotaxi/assets/pointer.png' |
| | | import rightIcon from '../../../robotaxi/assets/right-icon.png' |
| | |
| | | getCityLocation, |
| | | mapOrderStatusToTripStatus, |
| | | safeParse, |
| | | getWeatherInfo |
| | | } from '../../../utils/Robotaxi-public-method'; |
| | | import { _ROBOTAXI_WS_HOST, CDN } from '../../../constants/api'; |
| | | // 城市切换状态同步 |
| | |
| | | const [hotfixEndText, setHotfixEndText] = useState(''); |
| | | const [tripStatus, setTripStatus] = useState<number>(0); // 行程状态 |
| | | const [bannerList, setBannerList] = useState<any[]>([]); // 轮播图列表 |
| | | const [weatherData, setWeatherData] = useState<any>(null); // 天气数据 |
| | | const [orderData, setOrderData] = useState<any>({}); // 订单数据 |
| | | const [isLogin, setIsLogin] = useState(false); // 是否登录 |
| | | const { |
| | |
| | | toOrigin |
| | | }); // 首页启动编排 |
| | | |
| | | useEffect(() => { |
| | | if (selectedCity && selectedCity.name) { |
| | | getWeatherInfo(selectedCity.name).then(res => { |
| | | console.log('天气天气', res); |
| | | if (res && res.weather && res.temperature) { |
| | | setWeatherData({ |
| | | weather: res.weather.data, |
| | | temperature: res.temperature.data |
| | | }); |
| | | } |
| | | }).catch(err => { |
| | | console.error('获取天气失败', err); |
| | | }); |
| | | } |
| | | }, [selectedCity]); |
| | | |
| | | useDidShow(() => { |
| | | bootstrap.onDidShow(); |
| | | }); |
| | |
| | | if (targetMarker) { |
| | | // 忽略因调用 moveToLocation 引起的 region change,避免重复吸附计算 |
| | | shouldIgnoreRegionChange.current = true; |
| | | |
| | | |
| | | // 先隐藏tooltip,并触发指针跳动动画 |
| | | setShowToolTip(false); |
| | | setBounce(true); |
| | | |
| | | |
| | | // 直接更新经纬度实现跳转 |
| | | setLatitude(targetMarker.latitude); |
| | | setLongitude(targetMarker.longitude); |
| | |
| | | style={{ width: '100%', height: '85vh', flex: 'none' }} |
| | | > |
| | | {/* 选择城市 */} |
| | | <View onClick={toSelectCityPage} className="select-city"> |
| | | <Text>{selectedCity.name || '北京市'}</Text> |
| | | <Image className="select-city-icon" src={selectCityIcon} /> |
| | | <View className="select-city-container"> |
| | | <View onClick={toSelectCityPage} className="select-city"> |
| | | <Text>{selectedCity.name || '北京市'}</Text> |
| | | <Image className="select-city-icon" src={selectCityIcon} /> |
| | | </View> |
| | | {weatherData && ( |
| | | <View className="weather-info"> |
| | | <Image className="weather-icon" src={getWeatherIconByDesc(weatherData.weather)} /> |
| | | <Text>{weatherData.temperature}°C</Text> |
| | | </View> |
| | | )} |
| | | </View> |
| | | {/* 定位图标 */} |
| | | <View className="origin-box" style={{ opacity: originOpacity, transition: isDragging.current ? 'none' : 'opacity 0.2s ease-out' }}> |
| | |
| | | method: 'GET', |
| | | payload |
| | | }, true); |
| | | } |
| | | } |
| | | |
| | | // 收藏/取消收藏地址 |
| | | export const collectPoint = async (payload) => { |
| | | return request({ |
| | | url: _ROBOTAXI_HOST + '/account/api/v1/mini-program/collect/addr', |
| | | method: 'POST', |
| | | payload |
| | | }, true); |
| | | } |
| | | |
| | | // 获取用户收藏夹收藏地址列表 |
| | | export const getCollectPoints = async (payload) => { |
| | | return request({ |
| | | url: _ROBOTAXI_HOST + '/account/api/v1/mini-program/collect/addrlist', |
| | | method: 'GET', |
| | | payload |
| | | }, true); |
| | | } |
| | |
| | | } from '../../../utils/Robotaxi-public-method'; |
| | | import { getGeofence } from '../../api/home'; |
| | | import { getOrderDistance } from '../../api/select-map-site'; |
| | | import { collectPoint } from '../../api/site-search'; |
| | | import { checkOtp } from '../../../utils/tools'; |
| | | import { jumpTo } from '../../../utils/jump'; |
| | | |
| | |
| | | import './index.less'; |
| | | import closeIcon from '../../../../assets/close-icon.png'; |
| | | import deleteIcon from '../../../../assets/delete-icon.png'; |
| | | import { getCollectPoints, collectPoint } from '../../../../api/site-search'; |
| | | |
| | | type Props = { |
| | | show: boolean; |
| | | list: any[]; |
| | | onClose: () => void; |
| | | onDelete: (item: any) => void; |
| | | onSelect: (item: any) => void; |
| | | }; |
| | | |
| | | export default function FavoritePopup({ show, list, onClose, onDelete, onSelect }: Props) { |
| | | export default function FavoritePopup({ show, onClose, onSelect }: Props) { |
| | | const [list, setList] = useState<any[]>([]); |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const [thumbHeight, setThumbHeight] = useState(0); |
| | | const [thumbTop, setThumbTop] = useState(0); |
| | | const [containerHeight, setContainerHeight] = useState(0); |
| | | const [showScrollbar, setShowScrollbar] = useState(false); |
| | | |
| | | const fetchData = async () => { |
| | | try { |
| | | const res = await getCollectPoints({}); |
| | | if (res && Array.isArray(res)) { |
| | | setList(res); |
| | | } else if (res && res.data && Array.isArray(res.data)) { |
| | | setList(res.data); |
| | | } else if (res && res.list && Array.isArray(res.list)) { |
| | | setList(res.list); |
| | | } |
| | | } catch (e) { |
| | | console.log('获取收藏夹失败', e); |
| | | } |
| | | }; |
| | | |
| | | const handleDelete = async (item: any) => { |
| | | try { |
| | | await collectPoint({ |
| | | action: 2, // 假设 2 是取消收藏 |
| | | pointId: item.id || item.pointId, |
| | | }); |
| | | // 重新拉取数据或从列表中移除 |
| | | setList(prev => prev.filter(f => (f.id || f.pointId) !== (item.id || item.pointId))); |
| | | Taro.showToast({ title: '已取消收藏', icon: 'none' }); |
| | | } catch (e) { |
| | | console.log('取消收藏失败', e); |
| | | // 兼容接口失败或格式不对时也能移除(仅供开发时预览效果) |
| | | setList(prev => prev.filter(f => (f.id || f.pointId) !== (item.id || item.pointId))); |
| | | } |
| | | }; |
| | | |
| | | const onScroll = (e) => { |
| | | const { scrollHeight, scrollTop } = e.detail; |
| | |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | fetchData(); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | |
| | | <Image className='close-icon' src={closeIcon} onClick={onClose} /> |
| | | </View> |
| | | <View className='favorite-list-wrap'> |
| | | <ScrollView |
| | | scrollY |
| | | <ScrollView |
| | | scrollY |
| | | className='favorite-list' |
| | | enhanced |
| | | lowerThreshold={30} |
| | | upperThreshold={30} |
| | | enhanced |
| | | lowerThreshold={30} |
| | | upperThreshold={30} |
| | | showScrollbar={false} |
| | | onScroll={onScroll} |
| | | > |
| | |
| | | </View> |
| | | <Image onClick={(e) => { |
| | | e.stopPropagation(); |
| | | onDelete(item); |
| | | handleDelete(item); |
| | | }} className='delete-icon' src={deleteIcon} /> |
| | | </View> |
| | | ))} |
| | |
| | | </ScrollView> |
| | | {showScrollbar && ( |
| | | <View className='custom-scrollbar-track'> |
| | | <View |
| | | className='custom-scrollbar-thumb' |
| | | style={{ |
| | | <View |
| | | className='custom-scrollbar-thumb' |
| | | style={{ |
| | | height: `${thumbHeight}px`, |
| | | transform: `translateY(${thumbTop}px)` |
| | | }} |
| | | }} |
| | | /> |
| | | </View> |
| | | )} |
| | |
| | | const [showTipPopup, setShowTipPopup] = useState(false) |
| | | const [tipType, setTipType] = useState('') |
| | | const [showFavoritePopup, setShowFavoritePopup] = useState(false) |
| | | const [favoriteList, setFavoriteList] = useState<any[]>([]) |
| | | |
| | | const MAX_SHOW_COUNT = 5 // 默认最多显示5条 |
| | | const MAX_EXPANDED_COUNT = 10 |
| | |
| | | }); |
| | | } |
| | | }).catch(() => { }); |
| | | |
| | | // 模拟收藏夹数据,供展示样式 |
| | | setFavoriteList([ |
| | | { |
| | | id: 1, |
| | | name: '广州岭南职业技术学院广州岭南职业技术学院广州岭南职业技术学院', |
| | | address: '广州市天河区大观中路492号', |
| | | latitude: 23.17066, |
| | | longitude: 113.40939, |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: '广州岭南职业技术学院广州岭南职业技术学院广州岭南职业技术学院', |
| | | address: '广州市天河区大观中路492号', |
| | | latitude: 23.17066, |
| | | longitude: 113.40939, |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: '广州岭南职业技术学院广州岭南职业技术学院广州岭南职业技术学院', |
| | | address: '广州市天河区大观中路492号', |
| | | latitude: 23.17066, |
| | | longitude: 113.40939, |
| | | }, |
| | | { |
| | | id: 4, |
| | | name: '广州岭南职业技术学院广州岭南职业技术学院广州岭南职业技术学院', |
| | | address: '广州市天河区大观中路492号', |
| | | latitude: 23.17066, |
| | | longitude: 113.40939, |
| | | }, |
| | | { |
| | | id: 5, |
| | | name: '广州岭南职业技术学院广州岭南职业技术学院广州岭南职业技术学院', |
| | | address: '广州市天河区大观中路492号', |
| | | latitude: 23.17066, |
| | | longitude: 113.40939, |
| | | }, |
| | | { |
| | | id: 6, |
| | | name: '广州岭南职业技术学院广州岭南职业技术学院广州岭南职业技术学院', |
| | | address: '广州市天河区大观中路492号', |
| | | latitude: 23.17066, |
| | | longitude: 113.40939, |
| | | }, |
| | | ]); |
| | | }, []); |
| | | |
| | | const changeSearchValue = (e) => { |
| | |
| | | setTipType(''); |
| | | }, 600); |
| | | } |
| | | |
| | | const handleDeleteFavorite = (item) => { |
| | | setFavoriteList(prev => prev.filter(f => f.id !== item.id)); |
| | | }; |
| | | |
| | | const handleSelectFavorite = (item) => { |
| | | setShowFavoritePopup(false); |
| | |
| | | /> |
| | | <FavoritePopup |
| | | show={showFavoritePopup} |
| | | list={favoriteList} |
| | | onClose={() => setShowFavoritePopup(false)} |
| | | onDelete={handleDeleteFavorite} |
| | | onSelect={handleSelectFavorite} |
| | | /> |
| | | </View> |
| | |
| | | }); |
| | | }; |
| | | |
| | | // 获取天气信息 |
| | | export const getWeatherInfo = (city?: string): Promise<any> => { |
| | | return new Promise((resolve, reject) => { |
| | | const myAmapFun = new amapFile.AMapWX({ key: _AMAP_KEY }); |
| | | const options: any = { |
| | | success: (data) => resolve(data), |
| | | fail: (info) => reject(info) |
| | | }; |
| | | if (city) { |
| | | options.city = city; |
| | | } |
| | | myAmapFun.getWeather(options); |
| | | }); |
| | | }; |
| | | |
| | | // 格式化电子围栏数据 |
| | | export const formatGeofencePolygons = (geofenceList: any[]) => { |
| | | if (!geofenceList || !Array.isArray(geofenceList)) { |
| New file |
| | |
| | | import weatherIcon from '../robotaxi/assets/weather.png'; |
| | | |
| | | /** |
| | | * 根据高德天气返回的天气描述,匹配系统定义的天气图标。 |
| | | * 目前暂无真实切图,全部占位使用 weatherIcon,后续有了切图后替换返回的变量即可。 |
| | | * @param weatherDesc 高德天气返回的天气文字 |
| | | */ |
| | | export const getWeatherIconByDesc = (weatherDesc: string) => { |
| | | if (!weatherDesc) return weatherIcon; |
| | | |
| | | // 优先级建议:雷阵雨/冰雹 > 大雨/暴雨 > 小雨/中雨 > 雪 > 雨夹雪 > 沙尘 > 雾霾 > 大风 > 多云/阴 > 晴 |
| | | |
| | | // 雷阵雨/冰雹 |
| | | if (['雷阵雨', '强雷阵雨', '雷阵雨并伴有冰雹'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 大雨/暴雨 |
| | | if (['大雨', '暴雨', '大暴雨', '特大暴雨', '强阵雨', '极端降雨', '中雨-大雨', '大雨-暴雨', '暴雨-大暴雨', '大暴雨-特大暴雨'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 小雨/中雨 |
| | | if (['阵雨', '小雨', '中雨', '毛毛雨/细雨', '雨', '小雨-中雨', '阵雨天气'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 雪 |
| | | if (['雪', '阵雪', '小雪', '中雪', '大雪', '暴雪', '小雪-中雪', '中雪-大雪', '大雪-暴雪'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 雨夹雪 |
| | | if (['雨夹雪', '阵雨夹雪', '雨雪天气', '冻雨'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 沙尘/浮尘 |
| | | if (['浮尘', '扬沙', '沙尘暴', '强沙尘暴'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 雾/霾 |
| | | if (['雾', '浓雾', '强浓雾', '轻雾', '大雾', '特强浓雾', '霾', '中度霾', '重度霾', '严重霾'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 大风/台风 |
| | | if (['有风', '和风', '微风', '清风', '强风/劲风', '疾风', '大风', '烈风', '风暴', '狂暴风', '飓风', '热带风暴', '龙卷风'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 多云/阴 |
| | | if (['晴间多云', '多云', '阴'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | // 晴 |
| | | if (['晴', '少云', '平静'].includes(weatherDesc)) { |
| | | return weatherIcon; |
| | | } |
| | | |
| | | // 其他/未知:['热', '冷', '未知'] |
| | | return weatherIcon; |
| | | }; |