| src/pages/robotaxi/home/components/marketing-popup/index.less | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/robotaxi/home/components/marketing-popup/index.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/robotaxi/home/index.less | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/robotaxi/home/index.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/api/home.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/assets/coverImage.png | 补丁 | 查看 | 原始文档 | blame | 历史 |
src/pages/robotaxi/home/components/marketing-popup/index.less
@@ -4,7 +4,7 @@ left: 0; right: 0; bottom: 0; z-index: 10001; z-index: 100001; display: flex; justify-content: center; pointer-events: auto; @@ -29,7 +29,7 @@ left: -46px; width: 824px; height: 876px; z-index: 10002; z-index: 100002; opacity: 0; transform: scale(0.96); transition: opacity 0.26s ease, transform 0.26s ease; @@ -44,7 +44,7 @@ display: flex; align-items: center; justify-content: center; z-index: 10011; z-index: 1000011; } .marketing-popup-close-single { @@ -55,7 +55,7 @@ display: flex; align-items: center; justify-content: center; z-index: 10011; z-index: 1000011; } .marketing-popup-close-icon { @@ -69,9 +69,9 @@ left: 0; width: 560px; height: 658px; z-index: 10002; background: url(../../../../../robotaxi/assets/marketing-manyBg.png) no-repeat center center; z-index: 100002; background: url(../../../../../robotaxi/assets/marketing-manyBg.png) no-repeat center center; background-size: 100% 100%; } @@ -83,11 +83,15 @@ background: url(../../../../../robotaxi/assets/marketing-coupon-bg.png) no-repeat center center; background-size: 100% 100%; position: relative; .marketing-popup-item-left { width: 158px; box-sizing: border-box; height: 100%; padding: 20px 0 0 38px; padding-top: 20px; display: flex; flex-direction: column; align-items: center; .marketing-popup-item-price { display: flex; .marketing-popup-item-amount { @@ -165,11 +169,31 @@ margin-top: 12px; } } .marketing-popup-item-quantity { position: absolute; top: 0; right: 0; width: 52px; height: 34px; background: url(../../../../../robotaxi/assets/marketing-coupon-topBg.png) no-repeat center center; background-size: 100% 100%; font-family: PingFang SC; font-size: 28px; font-weight: 500; line-height: 28px; text-align: center; letter-spacing: normal; color: #ffffff; display: flex; justify-content: center; align-items: center; } } .marketing-popup-multi-panel { margin-top: 408px; z-index: 10010; z-index: 100010; width: 560px; height: auto; position: relative; @@ -182,7 +206,7 @@ .marketing-popup-panel { position: relative; z-index: 10010; z-index: 100010; } .marketing-popup-title { @@ -235,7 +259,7 @@ .marketing-popup-single-panel { margin-top: 388px; z-index: 10010; z-index: 100010; width: 540px; height: auto; background: url(../../../../../robotaxi/assets/marketing-singleBg.png) @@ -259,7 +283,7 @@ .marketing-popup-coupon-title-top-icon { width: 51px; height: 3px ; height: 3px; } } .marketing-popup-coupon-title { src/pages/robotaxi/home/components/marketing-popup/index.tsx
@@ -10,6 +10,7 @@ type MarketingPopupProps = { show: boolean; data: any; onClose: () => void; }; @@ -19,51 +20,9 @@ unit: string; title: string; expireText: string; tagText: string; conditionalAmount: number; quantity: string; }; const MARKETING_COUPONS: MarketingCoupon[] = [ { id: 'new-user-coupon', amount: '1', unit: '元', title: '1元乘车券', expireText: '2026.04.24 23:59到期', tagText: '满1元可用' }, // { // id: 'discount-coupon-1', // amount: '1', // unit: '折', // title: '1折出行券', // expireText: '2026.04.24 23:59到期', // tagText: '无门槛' // }, // { // id: 'discount-coupon-5', // amount: '5', // unit: '折', // title: '5折出行券', // expireText: '2026.04.24 23:59到期', // tagText: '无门槛' // }, // { // id: 'discount-coupon-5', // amount: '5', // unit: '折', // title: '5折出行券', // expireText: '2026.04.24 23:59到期', // tagText: '无门槛' // }, // { // id: 'discount-coupon-5', // amount: '5', // unit: '折', // title: '5折出行券', // expireText: '2026.04.24 23:59到期', // tagText: '无门槛' // } ]; export default function MarketingPopup(props: MarketingPopupProps) { const animationDuration = 260; @@ -92,8 +51,22 @@ }; }, [props.show]); const isSingleCoupon = MARKETING_COUPONS.length === 1; const isTwoCoupons = MARKETING_COUPONS.length === 2; const sourceCoupons: MarketingCoupon[] = Array.isArray(props.data?.coupons) ? props.data.coupons.map((item: any, index: number) => ({ id: String(item?.id ?? item?.couponId ?? index), amount: String(item?.amount ?? item?.discountAmount ?? '0'), unit: String(item?.unit ?? (Number(item?.couponType) === 1 ? '折' : '元')), title: String(item?.title ?? item?.couponName ?? '--'), expireText: item?.endTime ?? '--', quantity: String(item?.quantity ?? item?.count ?? '--'), conditionalAmount: Number(item?.conditionalAmount ?? 0) })) : []; const popupTitle = String(props.data?.miniProgramPopupTitle || '恭喜获得新人券'); const popupBtnText = String(props.data?.miniProgramBtnText || '立即领取'); const isSingleCoupon = sourceCoupons.length === 1; const isTwoCoupons = sourceCoupons.length === 2; const preventTouchEvent = (event: any) => { if (event?.stopPropagation) { event.stopPropagation(); @@ -101,6 +74,26 @@ if (event?.preventDefault) { event.preventDefault(); } }; 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}`; }; const formatThresholdText = (conditionalAmount?: number) => { const amount = Number(conditionalAmount || 0); if (amount > 0) { return `满 ${amount} 可用`; } return "无门槛"; }; const renderCouponItem = (coupon: MarketingCoupon) => { @@ -111,33 +104,35 @@ <Text className='marketing-popup-item-amount'>{coupon.amount}</Text> <Text className='marketing-popup-item-unit'>{coupon.unit}</Text> </View> <View className='marketing-popup-item-tag'>{coupon.tagText}</View> <View className='marketing-popup-item-tag'>{formatThresholdText(coupon.conditionalAmount)}</View> </View> <View className='marketing-popup-item-right'> <View className='marketing-popup-item-title'>{coupon.title}</View> <View className='marketing-popup-item-expire'>{coupon.expireText}</View> <View className='marketing-popup-item-expire'>{formatTime(coupon.expireText, "list")}到期</View> </View> <View className='marketing-popup-item-quantity'>x{coupon.quantity}</View> </View> ); }; const renderSingleCouponModule = () => { const coupon = sourceCoupons[0]; return ( <View className='marketing-popup-single-panel'> <View className='marketing-popup-title'> <Image className='marketing-popup-coupon-title-top-icon' src={singleTopLeftIcon} /> <View style={{ margin: '0 14px' }}>恭喜获得</View> <View style={{ margin: '0 14px' }}>恭喜获得{Number(coupon.quantity) > 0 ? coupon.quantity + '张' : ''}</View> <Image className='marketing-popup-coupon-title-top-icon' src={singleTopRightIcon} /> </View> <View className='marketing-popup-coupon-title'> <Image className='marketing-popup-coupon-title-icon' src={singleLeftIcon} /> <View style={{ margin: '0 8px' }}>媒体尝鲜券</View> <View style={{ margin: '0 8px' }}>{coupon?.title || '媒体尝鲜券'}</View> <Image className='marketing-popup-coupon-title-icon' src={singleRightIcon} /> </View> <View className='marketing-popup-coupon-tag-text'>无门槛</View> <View className='marketing-popup-coupon-expire-text'>2026.04.24 23:59到期</View> <View className='marketing-popup-coupon-tag-text'>{formatThresholdText(coupon.conditionalAmount)}</View> <View className='marketing-popup-coupon-expire-text'>{formatTime(coupon.expireText, "list")}到期</View> <View className='marketing-popup-action' onClick={props.onClose}> 立即领取 {popupBtnText} </View> <View className='marketing-popup-close-single' onClick={props.onClose}> <Image className='marketing-popup-close-icon' src={closeIcon} /> @@ -156,7 +151,7 @@ className={`marketing-popup-multi-bg ${isTwoCoupons ? 'marketing-popup-multi-bg--two' : ''}`} /> <View className='marketing-popup-panel'> <View className='marketing-popup-title'>恭喜获得新人券</View> <View className='marketing-popup-title'>{popupTitle}</View> <View className='marketing-popup-panel-content'> <ScrollView className='marketing-popup-list' @@ -164,12 +159,12 @@ enhanced showScrollbar={false} > {MARKETING_COUPONS.map(renderCouponItem)} {sourceCoupons.map(renderCouponItem)} <View style={{ height: '1px' }} /> </ScrollView> </View> <View className='marketing-popup-action' onClick={props.onClose}> 立即领取 {popupBtnText} </View> </View> </View> src/pages/robotaxi/home/index.less
@@ -189,6 +189,23 @@ } } .activity-float-window { position: fixed; right: 6px; bottom: 234px; z-index: 10020; width: 96px; height: 96px; display: flex; align-items: center; justify-content: center; .activity-float-window-img { width: 96px; height: 96px; } } .pointer-box { position: absolute; top: 50%; src/pages/robotaxi/home/index.tsx
@@ -12,6 +12,7 @@ import personalCenter from '../../../robotaxi/assets/personal-center.png' import coupon from '../../../robotaxi/assets/coupon.png' import contactService from '../../../robotaxi/assets/contact-service.png' import floatDefaultImg from '../../../robotaxi/assets/coverImage.png' import { getWeatherIconByDesc } from '../../../utils/weather'; import originIcon from '../../../robotaxi/assets/origin-icon.png' import pointer from '../../../robotaxi/assets/pointer.png' @@ -58,34 +59,16 @@ getAgreement, getOrderActive, getHotFixInfo, firstEnter firstEnter, getFloatActicity } from '../../../robotaxi/api/home'; import { getServicePhone } from '../../../robotaxi/api/customer-service'; import { getOrderDistance } from '../../../robotaxi/api/select-map-site'; import { checkOtp } from '../../../utils/tools'; import { checkOtp, toH5 } from '../../../utils/tools'; type HomeTripStatus = TripStatus | 0; export default function Home() { const resolveMarketingVisible = (response: any) => { if (typeof response === 'boolean') return response; if (typeof response === 'number') return response === 1; if (Array.isArray(response)) return response.length > 0; const data = response?.data ?? response; if (typeof data === 'boolean') return data; if (typeof data === 'number') return data === 1; if (Array.isArray(data)) return data.length > 0; return Boolean( data?.showMarketing ?? data?.showPopup ?? data?.isShow ?? data?.isFirstEnter ?? data?.firstEnter ); }; const renderTailPreserveText = (text: string, tailCount = 3) => { const safeText = String(text || ''); if (!safeText) return null; @@ -103,6 +86,10 @@ }; const [showMarketing, setShowMarketing] = useState(false); // 营销弹窗是否显示 const [marketingData, setMarketingData] = useState({}); // 营销弹窗数据 const [showFloatActivity, setShowFloatActivity] = useState(false); // 活动悬浮窗是否显示 const [floatActivityUrl, setFloatActivityUrl] = useState(''); // 活动悬浮窗跳转地址 const [floatActivityImg, setFloatActivityImg] = useState(floatDefaultImg); // 活动悬浮窗图片 const [showPrivacy, setShowPrivacy] = useState(false); // 隐私政策弹窗是否显示 const [showCustomerService, setShowCustomerService] = useState(false); // 客服弹窗是否显示 const mapState = useMapState(); // 地图状态容器 @@ -415,18 +402,40 @@ .then((hasLogin) => { if (!hasLogin) { setShowMarketing(false); setShowFloatActivity(false); return; } return firstEnter() .then((res: any) => { setShowMarketing(resolveMarketingVisible(res)); return Promise.all([firstEnter(), getFloatActicity()]) .then(([firstEnterRes, floatRes]: any[]) => { // 兼容 firstEnter 接口返回结构:可能是 res.data 或直接为业务对象 const firstEnterData = firstEnterRes?.data ?? firstEnterRes ?? {}; const show = Boolean(firstEnterData?.isFirst); setShowMarketing(show); setMarketingData(firstEnterData); const floatData = floatRes?.data ?? floatRes ?? {}; const isFloatActivity = Boolean( floatData?.enableFloatWindow === 1 ); setShowFloatActivity(isFloatActivity); setFloatActivityUrl( String( floatData?.floatWindowJumpUrl || '' ) ); const img = String(floatData?.coverImage || ''); setFloatActivityImg(img || floatDefaultImg); }) .catch(() => { setShowMarketing(false); setShowFloatActivity(false); setFloatActivityUrl(''); }); }) .catch(() => { setShowMarketing(false); setShowFloatActivity(false); setFloatActivityUrl(''); }); if (typeof latitude === 'number' && typeof longitude === 'number') { tryStartPollingOnInit({ @@ -506,6 +515,15 @@ const handleCloseCustomerService = () => { setShowCustomerService(false); }; const handleFloatActivityClick = () => { if (!floatActivityUrl) { Taro.showToast({ title: '活动页面建设中', icon: 'none' }); return; } // 预留 H5 出口,后续可按活动类型扩展埋点和鉴权逻辑 toH5(floatActivityUrl, true); }; // 初始化浮层锚点:固定在 content-box 顶部上方 32px @@ -759,7 +777,18 @@ show={showCustomerService} onClose={handleCloseCustomerService} /> <MarketingPopup show={showMarketing} onClose={() => setShowMarketing(false)} /> {/* 营销活动弹窗 */} <MarketingPopup show={showMarketing} data={marketingData} onClose={() => setShowMarketing(false)} /> {/* 悬浮窗 */} {showFloatActivity && ( <View className='activity-float-window' onClick={handleFloatActivityClick}> <Image className='activity-float-window-img' src={floatActivityImg || floatDefaultImg} onError={() => setFloatActivityImg(floatDefaultImg)} /> </View> )} </View> ) } src/robotaxi/api/home.ts
@@ -118,3 +118,11 @@ method: 'GET' }, true); } // 获取是否显示悬浮窗 export const getFloatActicity = async () => { return request({ url: _ROBOTAXI_HOST + '/operation/api/v1/mini-program/acticity/getFloatActicity', method: 'GET' }, true); } src/robotaxi/assets/coverImage.png