From 54fa530078b426af0ed945fba7bd5c10fe907d0e Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期五, 06 二月 2026 14:23:24 +0800
Subject: [PATCH] feat(begin-route): 新增行程开始页面及订单状态卡片
---
src/robotaxi/pages/begin-route/index.less | 227 +++++++
src/robotaxi/pages/begin-route/index.config.ts | 4
src/robotaxi/pages/site-search/index.tsx | 22
src/robotaxi/pages/begin-route/components/css-folder/passenger-notice.less | 71 ++
src/robotaxi/pages/begin-route/components/evaluate-popup.tsx | 48 +
src/robotaxi/pages/begin-route/components/release-route.tsx | 0
src/robotaxi/pages/begin-route/components/css-folder/evaluate-popup.less | 0
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less | 0
src/robotaxi/pages/begin-route/components/announcements-popup.tsx | 0
src/robotaxi/pages/begin-route/components/passenger-notice.tsx | 65 ++
src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less | 53 +
src/robotaxi/pages/begin-route/components/css-folder/release-be-being.less | 0
src/robotaxi/pages/begin-route/components/over-route.tsx | 0
src/robotaxi/pages/select-map-site/index.less | 154 ----
src/pages/robotaxi/home/index.tsx | 56 +
src/robotaxi/pages/begin-route/components/css-folder/announcements-popup.less | 0
src/robotaxi/pages/begin-route/index.tsx | 483 +++++++++++++++
src/robotaxi/pages/select-map-site/components/tip-popup.tsx | 2
src/robotaxi/pages/begin-route/components/release-be-being.tsx | 0
src/robotaxi/pages/begin-route/components/calling-car.tsx | 0
/dev/null | 59 -
src/app.config.ts | 3
src/robotaxi/assets/dotted-line.png | 0
src/robotaxi/pages/select-map-site/index.tsx | 344 +---------
src/pages/robotaxi/home/index.less | 120 +++
src/robotaxi/pages/begin-route/components/cancel-popup.tsx | 99 +++
src/robotaxi/assets/order-card-bg.png | 0
src/robotaxi/pages/begin-route/components/css-folder/over-route.less | 0
src/robotaxi/pages/begin-route/components/css-folder/release-route.less | 0
29 files changed, 1,280 insertions(+), 530 deletions(-)
diff --git a/src/app.config.ts b/src/app.config.ts
index 8215280..20fe7f9 100644
--- a/src/app.config.ts
+++ b/src/app.config.ts
@@ -26,7 +26,8 @@
'pages/order/index',
'pages/order/detail/index',
'pages/select-city/index',
- 'pages/select-map-site/index'
+ 'pages/select-map-site/index',
+ 'pages/begin-route/index'
],
}
];
diff --git a/src/pages/robotaxi/home/index.less b/src/pages/robotaxi/home/index.less
index 0ec6b39..ce44250 100644
--- a/src/pages/robotaxi/home/index.less
+++ b/src/pages/robotaxi/home/index.less
@@ -339,6 +339,126 @@
}
}
+ @keyframes radiate {
+ 0% {
+ transform: translate(-50%, -50%) scale(0.5);
+ opacity: 1;
+ }
+
+ 100% {
+ transform: translate(-50%, -50%) scale(1.2);
+ opacity: 0;
+ }
+ }
+
+ .order-status {
+ position: relative;
+ margin-top: 24px;
+ margin-bottom: 6px;
+ border-radius: 24px;
+ background: #FEFEFE;
+ box-sizing: border-box;
+ border: 0px solid #FFFFFF;
+ height: 104px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-right: 24px;
+
+ &-left {
+ display: flex;
+ align-items: center;
+
+ &-item {
+ margin-right: 12px;
+ position: relative;
+ margin-left: 14px;
+ display: flex;
+ justify-content: center;
+ width: 78px;
+ height: 78px;
+
+ view {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ border-radius: 50%;
+ width: 78px;
+ height: 78px;
+ background: rgba(64, 124, 255, 0.2);
+ animation: radiate 2s infinite ease-out;
+ }
+
+ view:last-child {
+ animation-delay: 1s;
+ }
+
+ &-icon {
+ position: relative;
+ z-index: 9;
+ width: 26px;
+ height: 38px;
+ }
+ }
+
+ &-text {
+ font-family: PingFang SC;
+ font-size: 28px;
+ font-weight: 500;
+ line-height: 48px;
+ letter-spacing: normal;
+ color: #3D3D3D;
+ }
+
+ &-icon {
+ width: 156px;
+ height: 73.54px;
+ margin-left: 18px;
+ flex-shrink: 0;
+ }
+
+ &-detail {
+ margin-left: 16px;
+
+ view:first-child {
+ font-size: 28px;
+ font-weight: 600;
+ line-height: 44px;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.9);
+ }
+
+ view:last-child {
+ font-size: 20px;
+ line-height: 20px;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.4);
+ }
+ }
+ }
+
+ &-btn {
+ position: relative;
+ z-index: 1;
+ line-height: 56px;
+ padding: 0 28px;
+ font-size: 24px;
+ letter-spacing: normal;
+ color: #FFFFFF;
+ border-radius: 200px;
+ background: linear-gradient(111deg, #8475FF 0%, #7DD6FF 98%);
+ }
+
+ &-bg {
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ width: 266px;
+ height: 96px;
+ }
+ }
+
.activity {
margin-top: 24px;
diff --git a/src/pages/robotaxi/home/index.tsx b/src/pages/robotaxi/home/index.tsx
index a1eb6ad..056724f 100644
--- a/src/pages/robotaxi/home/index.tsx
+++ b/src/pages/robotaxi/home/index.tsx
@@ -27,6 +27,7 @@
const [markers, setMarkers] = useState<any[]>([]); // 标记点列表
const [isMaintain, setIsMaintain] = useState(false); // 是否维护中
const [isOperatingArea, setIsOperatingArea] = useState(false); // 是否在运营区域内
+ const [tripStatus, setTripStatus] = useState<number>(0); // 行程状态
const [boxHeight, setBoxHeight] = useState<number | string>('auto'); // 活动区域高度
const startY = useRef(0); // 拖动开始时的Y坐标
@@ -37,11 +38,23 @@
const [selectedCity, setSelectedCity] = useState(''); // 选中的城市
+ const ORDER_STATUS = {
+ 4: '车辆正在赶来',
+ 5: '正在前往目的地',
+ 6: '车辆已到达上车点,请尽快前往',
+ 7: '车辆已到达目的地,请尽快结束行程',
+ }
+
useDidShow(() => {
// 从本地存储中获取选中的城市
const selectedCity = Taro.getStorageSync('selectedCity');
if (selectedCity) {
setSelectedCity(selectedCity);
+ }
+ // 从本地存储中获取行程状态
+ const tripStatus = Taro.getStorageSync('tripStatus');
+ if (tripStatus) {
+ setTripStatus(Number(tripStatus));
}
});
@@ -187,10 +200,10 @@
mapCtx.getCenterLocation({
success: (res) => {
const { latitude: lat, longitude: lng } = res;
-
- const { targetMarker, minDistance } = findNearestMarker(lat, lng, markers);
- // 如果最近的点在一定范围内(例如 200米),则吸附
+ const { targetMarker, minDistance } = findNearestMarker(lat, lng, markers);
+
+ // 如果最近的点在一定范围内(例如 200米),则吸附
if (targetMarker && minDistance < 200) {
// 使用 moveToLocation 实现吸附效果
mapCtx.moveToLocation({
@@ -257,13 +270,19 @@
const toSeleteSitePage = (type: string) => {
Taro.navigateTo({
- url: '/robotaxi/pages/site-search/index?type=' + type
+ url: '/robotaxi/pages/site-search/index?pageType=' + type
})
}
const toSeleteCityPage = () => {
Taro.navigateTo({
url: '/robotaxi/pages/select-city/index',
+ })
+ }
+
+ const toOrderDetail = () => {
+ Taro.navigateTo({
+ url: '/robotaxi/pages/begin-route/index',
})
}
@@ -366,6 +385,35 @@
</View>
</View>
</View>
+ {/* 订单状态卡片 */}
+ {
+ [2, 3, 4, 5, 6, 7].includes(tripStatus) &&
+ <View className="order-status">
+ {
+ [2, 3].includes(tripStatus) &&
+ <View className="order-status-left">
+ <View className="order-status-left-item">
+ <Image className="order-status-left-item-icon" src={require('../../../robotaxi/assets/pointer.png')} />
+ <View></View>
+ <View></View>
+ </View>
+ <View className="order-status-left-text">正在为您呼叫...</View>
+ </View>
+ }
+ {
+ [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' />
+ <View className="order-status-left-detail">
+ <View>粤PXV7029</View>
+ <View>{ORDER_STATUS[tripStatus]}</View>
+ </View>
+ </View>
+ }
+ <View onClick={toOrderDetail} className="order-status-btn">查看</View>
+ <Image className="order-status-bg" src={require('../../../robotaxi/assets/order-card-bg.png')} />
+ </View>
+ }
{/* 运营活动 */}
<View className="activity">
{
diff --git a/src/robotaxi/assets/dotted-line.png b/src/robotaxi/assets/dotted-line.png
new file mode 100644
index 0000000..21fd187
--- /dev/null
+++ b/src/robotaxi/assets/dotted-line.png
Binary files differ
diff --git a/src/robotaxi/assets/order-card-bg.png b/src/robotaxi/assets/order-card-bg.png
new file mode 100644
index 0000000..8975486
--- /dev/null
+++ b/src/robotaxi/assets/order-card-bg.png
Binary files differ
diff --git a/src/robotaxi/pages/select-map-site/components/announcements-popup.tsx b/src/robotaxi/pages/begin-route/components/announcements-popup.tsx
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/announcements-popup.tsx
rename to src/robotaxi/pages/begin-route/components/announcements-popup.tsx
diff --git a/src/robotaxi/pages/select-map-site/components/calling-car.tsx b/src/robotaxi/pages/begin-route/components/calling-car.tsx
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/calling-car.tsx
rename to src/robotaxi/pages/begin-route/components/calling-car.tsx
diff --git a/src/robotaxi/pages/begin-route/components/cancel-popup.tsx b/src/robotaxi/pages/begin-route/components/cancel-popup.tsx
new file mode 100644
index 0000000..72bf0c0
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/components/cancel-popup.tsx
@@ -0,0 +1,99 @@
+import Taro from '@tarojs/taro';
+import { View, Text, Image } from '@tarojs/components';
+import { useEffect, useState } from 'react';
+import './css-folder/cancel-popup.less'
+
+type Props = {
+ show?: boolean;
+ cancelType?: number;
+ onClose?: () => void;
+ onConfirm?: () => void;
+}
+
+export default function CancelPopup({ show = false, cancelType = 1, onClose, onConfirm }: Props) {
+ const [visible, setVisible] = useState(false);
+ const [animationData, setAnimationData] = useState<any>();
+
+ const STATUS_MAP = {
+ 1: {
+ title: '2分钟内预计有车,确定取消吗?',
+ tip: '本单限时优惠16.2元,重新叫车可能等待更久',
+ },
+ 2: {
+ title: '正在为您重新派车中...',
+ tip: '当前车辆无法继续服务',
+ },
+ 3: {
+ title: '附近没有空余车辆,已为您取消订单',
+ tip: '当前运力紧张,建议您稍后重新发起订单',
+ },
+ 4: {
+ title: '订单已取消',
+ tip: '已为您取消订单,期待您下次体验',
+ },
+ }
+
+ useEffect(() => {
+ if (show && !visible) {
+ setVisible(true);
+ const animation = Taro.createAnimation({
+ duration: 300,
+ timingFunction: 'ease-out'
+ });
+ animation.translateY(0).step();
+ setAnimationData(animation.export());
+ } else if (!show && visible) {
+ const animation = Taro.createAnimation({
+ duration: 300,
+ timingFunction: 'ease-in'
+ });
+ animation.translateY(500).step();
+ setAnimationData(animation.export());
+
+ setTimeout(() => {
+ setVisible(false);
+ }, 300);
+ }
+ }, [show]);
+
+ if (!visible) return null;
+
+ return (
+ <View className='cancel-popup' catchMove>
+ <View className='cancel-mask'></View>
+ <View className='cancel-content' animation={animationData}>
+ <View className='cancel-box'>
+ <Image onClick={onClose} className='cancel-icon' src={require('../../../assets/close-icon.png')} />
+ </View>
+ <View className='cancel-tip-box'>
+ <View className='cancel-tip-box-title'>{STATUS_MAP[cancelType].title}</View>
+ <View className={`cancel-tip-box-desc ${cancelType === 4 && 'color1'}`}>{STATUS_MAP[cancelType].tip}</View>
+ </View>
+ {
+ [3, 4].includes(cancelType) ?
+ <View>
+ <View className='cancel-site-box'>
+ <View className='cancel-site-box-icon'>
+ <View className='cancel-site-box-icon-start'></View>
+ <Image src={require('../../../assets/dotted-line.png')} />
+ <View className='cancel-site-box-icon-end'></View>
+ </View>
+ <View className='cancel-site-box-content'>
+ <View>广州岭南职业技术学院-南1门(公交站)</View>
+ <View>小鹏科技园2号楼</View>
+ </View>
+ </View>
+ <View className='cancel-btn-box'>
+ <View className='cancel-btn-again' onClick={onConfirm}>再来一单</View>
+ </View>
+ </View>
+ :
+ <View className='cancel-btn-box'>
+ <View className='cancel-btn-cancel' onClick={onConfirm}>确认取消</View>
+ <View className='cancel-btn-again' onClick={onClose}>再等等</View>
+ </View>
+ }
+ </View>
+ </View>
+ )
+}
\ No newline at end of file
diff --git a/src/robotaxi/pages/select-map-site/components/css-folder/announcements-popup.less b/src/robotaxi/pages/begin-route/components/css-folder/announcements-popup.less
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/css-folder/announcements-popup.less
rename to src/robotaxi/pages/begin-route/components/css-folder/announcements-popup.less
diff --git a/src/robotaxi/pages/select-map-site/components/css-folder/calling-car.less b/src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/css-folder/calling-car.less
rename to src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
diff --git a/src/robotaxi/pages/select-map-site/components/css-folder/cancel-popup.less b/src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less
similarity index 61%
rename from src/robotaxi/pages/select-map-site/components/css-folder/cancel-popup.less
rename to src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less
index 1eab928..1420f33 100644
--- a/src/robotaxi/pages/select-map-site/components/css-folder/cancel-popup.less
+++ b/src/robotaxi/pages/begin-route/components/css-folder/cancel-popup.less
@@ -53,6 +53,59 @@
letter-spacing: normal;
color: #F58900;
}
+
+ .color1 {
+ color: rgba(0, 0, 0, 0.4)
+ }
+ }
+
+ .cancel-site-box {
+ margin-top: 32px;
+ border-radius: 20px;
+ background: #F5F6FA;
+ padding: 32px;
+ display: flex;
+ gap: 16px;
+
+ &-icon {
+ padding: 17px 0;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ &-start {
+ width: 10px;
+ height: 10px;
+ background: #768AFF;
+ border-radius: 50%;
+ }
+
+ &-end {
+ width: 10px;
+ height: 10px;
+ background: #8BB654;
+ border-radius: 50%;
+ }
+
+ image {
+ flex: 1;
+ width: 2px;
+ margin: 7px 0;
+ }
+ }
+
+ &-content {
+ flex: 1;
+ font-size: 28px;
+ line-height: 44px;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.9);
+
+ view:first-child {
+ margin-bottom: 40px;
+ }
+ }
}
.cancel-btn-box {
diff --git a/src/robotaxi/pages/begin-route/components/css-folder/evaluate-popup.less b/src/robotaxi/pages/begin-route/components/css-folder/evaluate-popup.less
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/components/css-folder/evaluate-popup.less
diff --git a/src/robotaxi/pages/select-map-site/components/css-folder/over-route.less b/src/robotaxi/pages/begin-route/components/css-folder/over-route.less
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/css-folder/over-route.less
rename to src/robotaxi/pages/begin-route/components/css-folder/over-route.less
diff --git a/src/robotaxi/pages/begin-route/components/css-folder/passenger-notice.less b/src/robotaxi/pages/begin-route/components/css-folder/passenger-notice.less
new file mode 100644
index 0000000..35aa873
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/components/css-folder/passenger-notice.less
@@ -0,0 +1,71 @@
+.passenger-popup {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 99999;
+
+ .passenger-mask {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ }
+
+ .passenger-content {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: #ffffff;
+ border-radius: 16px 16px 0 0;
+ padding: 48px 45px 88px 45px;
+
+ &-title {
+ font-size: 32px;
+ font-weight: 500;
+ line-height: 48px;
+ text-align: center;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.9);
+ }
+
+ &-desc {
+ margin-top: 34px;
+ font-size: 28px;
+ line-height: 48px;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.9);
+ }
+
+ &-checkbox {
+ margin-top: 32px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 24px;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.6);
+ width: fit-content;
+
+ image {
+ width: 36px;
+ height: 36px;
+ }
+ }
+
+ &-btn {
+ margin-top: 30px;
+ border-radius: 16px;
+ background: linear-gradient(96deg, #8475FF -1%, #7DD6FF 103%);
+ line-height: 96px;
+ text-align: center;
+ font-size: 30px;
+ letter-spacing: normal;
+ color: #FFFFFF;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/robotaxi/pages/select-map-site/components/css-folder/release-be-being.less b/src/robotaxi/pages/begin-route/components/css-folder/release-be-being.less
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/css-folder/release-be-being.less
rename to src/robotaxi/pages/begin-route/components/css-folder/release-be-being.less
diff --git a/src/robotaxi/pages/select-map-site/components/css-folder/release-route.less b/src/robotaxi/pages/begin-route/components/css-folder/release-route.less
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/css-folder/release-route.less
rename to src/robotaxi/pages/begin-route/components/css-folder/release-route.less
diff --git a/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx b/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
new file mode 100644
index 0000000..ff44cc3
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
@@ -0,0 +1,48 @@
+import Taro from '@tarojs/taro';
+import { View, Text, Image } from '@tarojs/components';
+import { useEffect, useState } from 'react';
+import './css-folder/evaluate-popup.less'
+
+type Props = {
+ show?: boolean;
+ onClose?: () => void;
+ onConfirm?: () => void;
+}
+
+export default function EvaluatePopup({ show = false, onClose, onConfirm }: Props) {
+ const [visible, setVisible] = useState(false);
+ const [animationData, setAnimationData] = useState<any>();
+
+ useEffect(() => {
+ if (show && !visible) {
+ setVisible(true);
+ const animation = Taro.createAnimation({
+ duration: 300,
+ timingFunction: 'ease-out'
+ });
+ animation.translateY(0).step();
+ setAnimationData(animation.export());
+ } else if (!show && visible) {
+ const animation = Taro.createAnimation({
+ duration: 300,
+ timingFunction: 'ease-in'
+ });
+ animation.translateY(500).step();
+ setAnimationData(animation.export());
+
+ setTimeout(() => {
+ setVisible(false);
+ }, 300);
+ }
+ }, [show]);
+
+ if (!visible) return null;
+
+ return (
+ <View className='cancel-popup' catchMove>
+ <View className='cancel-mask'></View>
+ <View className='cancel-content' animation={animationData}>
+ </View>
+ </View>
+ )
+}
\ No newline at end of file
diff --git a/src/robotaxi/pages/select-map-site/components/over-route.tsx b/src/robotaxi/pages/begin-route/components/over-route.tsx
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/over-route.tsx
rename to src/robotaxi/pages/begin-route/components/over-route.tsx
diff --git a/src/robotaxi/pages/begin-route/components/passenger-notice.tsx b/src/robotaxi/pages/begin-route/components/passenger-notice.tsx
new file mode 100644
index 0000000..fbd0d24
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/components/passenger-notice.tsx
@@ -0,0 +1,65 @@
+import Taro from '@tarojs/taro';
+import { View, Text, Image } from '@tarojs/components';
+import { useEffect, useState } from 'react';
+import './css-folder/passenger-notice.less'
+
+type Props = {
+ show?: boolean;
+ onClose?: () => void;
+}
+
+export default function PassengerNotice({ show = false, onClose }: Props) {
+ const [visible, setVisible] = useState(false);
+ const [animationData, setAnimationData] = useState<any>();
+ const [checked, setChecked] = useState(false);
+
+ useEffect(() => {
+ if (show && !visible) {
+ setVisible(true);
+ const animation = Taro.createAnimation({
+ duration: 300,
+ timingFunction: 'ease-out'
+ });
+ animation.translateY(0).step();
+ setAnimationData(animation.export());
+ } else if (!show && visible) {
+ const animation = Taro.createAnimation({
+ duration: 300,
+ timingFunction: 'ease-in'
+ });
+ animation.translateY(500).step();
+ setAnimationData(animation.export());
+
+ setTimeout(() => {
+ setVisible(false);
+ }, 300);
+ }
+ }, [show]);
+
+ if (!visible) return null;
+
+ return (
+ <View className='passenger-popup' catchMove>
+ <View className='passenger-mask'></View>
+ <View className='passenger-content' animation={animationData}>
+ <View className='passenger-content-title'>乘车需知</View>
+ <View className='passenger-content-desc'>此处限停,请在倒计时结束前到达上车点此处限停,请在倒计时结束前到达上车点此处限停,请在倒计时结束前到达上车点此处限停,请在倒计时结束前到达上车点此处限停,请在倒计时结束前到达上车点此处限停,请在倒计时结束前到达上车点此请在倒计时结束前到达上车点此请在倒计时结束前到达上车点此</View>
+ <View onClick={() => setChecked(!checked)} className='passenger-content-checkbox'>
+ {
+ checked ?
+ <Image src={require('../../../assets/check.png')} />
+ :
+ <Image src={require('../../../assets/uncheck.png')} />
+ }
+ <View>3个月内不再显示</View>
+ </View>
+ <View onClick={() => {
+ if (checked) {
+ Taro.setStorageSync('passengerNoticeCheckedTime', Date.now());
+ }
+ onClose?.();
+ }} className='passenger-content-btn'>我知道了</View>
+ </View>
+ </View>
+ )
+}
\ No newline at end of file
diff --git a/src/robotaxi/pages/select-map-site/components/release-be-being.tsx b/src/robotaxi/pages/begin-route/components/release-be-being.tsx
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/release-be-being.tsx
rename to src/robotaxi/pages/begin-route/components/release-be-being.tsx
diff --git a/src/robotaxi/pages/select-map-site/components/release-route.tsx b/src/robotaxi/pages/begin-route/components/release-route.tsx
similarity index 100%
rename from src/robotaxi/pages/select-map-site/components/release-route.tsx
rename to src/robotaxi/pages/begin-route/components/release-route.tsx
diff --git a/src/robotaxi/pages/begin-route/index.config.ts b/src/robotaxi/pages/begin-route/index.config.ts
new file mode 100644
index 0000000..c9ae30e
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/index.config.ts
@@ -0,0 +1,4 @@
+export default {
+ navigationStyle: "custom",
+ disableScroll: true
+};
diff --git a/src/robotaxi/pages/begin-route/index.less b/src/robotaxi/pages/begin-route/index.less
new file mode 100644
index 0000000..4c30d99
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/index.less
@@ -0,0 +1,227 @@
+.select-city {
+ position: fixed;
+ top: 110px;
+ left: 31px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 72px;
+ height: 72px;
+ border-radius: 200px;
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(40px);
+ box-shadow: 0px 4px 16px 0px rgba(84, 92, 143, 0.1);
+
+ .select-city-icon {
+ width: 40px;
+ height: 40px;
+ }
+}
+
+.map-entry {
+ position: fixed;
+ top: 236px;
+ right: 32px;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ gap: 8px;
+ border-radius: 138px;
+ background: rgba(255, 255, 255, 1);
+ overflow: hidden;
+ padding: 8px 12px;
+
+ .map-entry-icon {
+ width: 64px;
+ height: 64px;
+ }
+
+ .split-line {
+ width: 44px;
+ height: 1px;
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+}
+
+.home-container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ overflow: hidden;
+ position: relative;
+}
+
+.container {
+ width: 100%;
+ position: absolute;
+ bottom: 0;
+ z-index: 10;
+ overflow: hidden;
+
+ .origin-box {
+ display: flex;
+ justify-content: flex-end;
+ padding-bottom: 32px;
+ padding-right: 32px;
+
+ .origin-icon {
+ width: 88px;
+ height: 88px;
+ }
+ }
+}
+
+.custom-callout {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.custom-callout-content {
+ display: flex;
+ align-items: center;
+ border-radius: 16px;
+ background: rgba(255, 255, 255, 0.9);
+ padding: 12px 12px 12px 20px;
+ font-size: 24px;
+ font-weight: bold;
+ color: rgba(0, 0, 0, 0.9);
+ line-height: 48px;
+ box-shadow: 0px 4px 16px 0px rgba(45, 48, 64, 0.2);
+}
+
+.car-time {
+ font-size: 24px;
+ letter-spacing: normal;
+ color: #3D3D3D;
+}
+
+.car-time-num {
+ font-size: 36px;
+ font-weight: 600;
+ letter-spacing: normal;
+ color: #6D7FD9;
+}
+
+.custom-callout-icon {
+ margin-left: 8px;
+ width: 32px;
+ height: 32px;
+ flex-shrink: 0;
+ display: block;
+}
+
+.waiting-callout {
+ position: relative;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ overflow: visible; // 确保辐射动画不被裁剪
+}
+
+.waiting-radiation-box {
+ position: absolute;
+ top: 100%; // 移到气泡下方
+ left: 50%;
+ margin-top: 30px; // 向下偏移以对齐Marker锚点(预估值)
+ width: 0;
+ height: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ pointer-events: none;
+ overflow: visible;
+}
+
+@keyframes ripple {
+ 0% {
+ opacity: 0.6;
+ transform: translate(-50%, -50%) scale(0.2);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: translate(-50%, -50%) scale(2.5);
+ }
+}
+
+.waiting-radiation-ring {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 200px;
+ height: 200px;
+ border-radius: 50%;
+ background: rgba(26, 79, 255, 0.05);
+ border: 1px solid rgba(26, 79, 255, 0.2);
+ opacity: 0; // 初始不可见,由动画控制
+ animation: ripple 3.5s infinite linear;
+
+ &:nth-child(1) {
+ animation-delay: 0s;
+ }
+
+ &:nth-child(2) {
+ animation-delay: 1s;
+ }
+}
+
+
+.waiting-callout-bg {
+ position: relative;
+ height: 70px;
+ min-width: 155px;
+ text-align: center;
+ width: fit-content;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.waiting-callout-bg-img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1;
+}
+
+.waiting-callout-bg-text {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 100;
+ font-size: 22px;
+ font-weight: 500;
+ letter-spacing: normal;
+ color: #FFFFFF;
+ line-height: 48px;
+ padding: 0 20px;
+ box-sizing: border-box;
+}
+
+.waiting-callout-txt {
+ position: relative;
+ z-index: 200;
+ margin-top: -26px;
+ display: flex;
+ align-items: center;
+ border-radius: 16px;
+ background: rgba(255, 255, 255, 0.9);
+ padding: 12px 12px 12px 20px;
+ min-width: 155px;
+}
+
+.waiting-callout-txt-content {
+ font-size: 24px;
+ font-weight: bold;
+ letter-spacing: normal;
+ color: rgba(0, 0, 0, 0.9);
+ line-height: 48px;
+}
\ No newline at end of file
diff --git a/src/robotaxi/pages/begin-route/index.tsx b/src/robotaxi/pages/begin-route/index.tsx
new file mode 100644
index 0000000..398863d
--- /dev/null
+++ b/src/robotaxi/pages/begin-route/index.tsx
@@ -0,0 +1,483 @@
+import Taro from "@tarojs/taro"
+import { useEffect, useState } from 'react';
+import { View, Map, Text, Image, CoverView, CoverImage } from '@tarojs/components';
+import './index.less';
+
+import CustomerServicePopup from '../../components/customer-service';
+import ReleaseRoute from './components/release-route';
+import ReleaseBeBeing from './components/release-be-being';
+import CancelPopup from './components/cancel-popup';
+import CallingCar from './components/calling-car';
+import AnnouncementsPopup from './components/announcements-popup';
+import OverRoute from './components/over-route';
+import PassengerNotice from './components/passenger-notice';
+
+import backIcon from '../../../robotaxi/assets/back-icon.png'
+import contactService from '../../../robotaxi/assets/contact-service.png'
+import coupon from '../../../robotaxi/assets/coupon.png'
+import suggest from '../../../robotaxi/assets/suggest.png'
+import originIcon from '../../../robotaxi/assets/origin-icon.png'
+import { parseRouteToPoints } from '../../../utils/Robotaxi-public-method';
+import amapFile from '../../../libs/amap-wx.130';
+import { AMAP_KEY } from '../../../constants/api';
+
+export default function SelectMapSite() {
+
+ const [showCustomerService, setShowCustomerService] = useState(false); // 客服弹窗是否显示
+ const [longitude, setLongitude] = useState<number | null>(null); // 经度
+ const [latitude, setLatitude] = useState<number | null>(null); // 纬度
+ const [markers, setMarkers] = useState<any[]>([]); // 标记点列表
+ const [polyline, setPolyline] = useState<any[]>([]); // 路线
+ const [startSite, setStartSite] = useState<any>(null); // 上车点
+ const [endSite, setEndSite] = useState<any>({}); // 下车点
+ const [tripStatus, setTripStatus] = useState<number>(Number(Taro.getStorageSync('tripStatus')) || 1); // 行程状态 1:发布行程 2:发布中 3:呼叫车中 4:车辆即将到达 5:车辆到达上车点 6:车辆行驶中 7:到达目的地 8:行程已结束 9:支付成功
+ const [elapsedSeconds, setElapsedSeconds] = useState(0); // 等待计时秒数
+ const [showCancelPopup, setShowCancelPopup] = useState(true); // 取消弹窗是否显示
+ const [showAnnouncementsPopup, setShowAnnouncementsPopup] = useState(false); // 安全注意事项动画弹窗是否显示
+ const [showOverRoutePopup, setShowOverRoutePopup] = useState(false); // 结束行程弹窗是否显示
+ const [showPassengerNotice, setShowPassengerNotice] = useState(false); // 乘车需知弹窗是否显示
+ const [cancelType, setCancelType] = useState(1); // 取消类型
+
+ const router = Taro.useRouter();
+
+
+ // 监听行程状态,处理等待计时
+ useEffect(() => {
+ if (tripStatus === 4) {
+ if (Taro.getStorageSync('passengerNoticeShowed') !== 'true') {
+ setShowPassengerNotice(true);
+ Taro.setStorageSync('passengerNoticeShowed', 'true');
+ }
+ if (Taro.getStorageSync('passengerNoticeCheckedTime')) {
+ const checkedTime = Number(Taro.getStorageSync('passengerNoticeCheckedTime'));
+ if (Date.now() - checkedTime > 3 * 30 * 24 * 60 * 60 * 1000) {
+ setShowPassengerNotice(true);
+ }
+ }
+ }
+ Taro.setStorageSync('tripStatus', tripStatus);
+ let timer: any = null;
+ if (tripStatus === 2 || tripStatus === 3) {
+ // 状态为2或3时,将地图中心定位到上车点
+ const s = Taro.getStorageSync('startSite');
+ if (s && s.latitude && s.longitude) {
+ setLatitude(s.latitude);
+ setLongitude(s.longitude);
+
+ // 增加延迟,确保地图上下文就绪
+ setTimeout(() => {
+ const mapCtx = Taro.createMapContext('map');
+ mapCtx.moveToLocation({
+ latitude: s.latitude,
+ longitude: s.longitude
+ });
+ }, 500);
+ }
+
+ setElapsedSeconds(0);
+ timer = setInterval(() => {
+ setElapsedSeconds(prev => prev + 1);
+ }, 1000);
+ } else {
+ setElapsedSeconds(0);
+ }
+ return () => {
+ if (timer) clearInterval(timer);
+ };
+ }, [tripStatus]);
+
+ // 页面初始化
+ useEffect(() => {
+ const s = Taro.getStorageSync('startSite');
+ const e = Taro.getStorageSync('endSite');
+ if (s) {
+ setStartSite(s);
+ }
+ if (e) {
+ setEndSite(e);
+ }
+
+ // 初始化默认标记点 (成都坐标)
+ const defaultMarkers = [
+ {
+ id: 1,
+ latitude: 30.572269,
+ longitude: 104.066541,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
+ width: 15,
+ height: 15,
+ anchor: { x: 0.5, y: 0.5 },
+ name: '中国南方电网·1号门'
+ },
+ {
+ id: 2,
+ latitude: 30.573200,
+ longitude: 104.068100,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
+ width: 15,
+ height: 15,
+ anchor: { x: 0.5, y: 0.5 },
+ name: '中国南方电网·2号门'
+ },
+ {
+ id: 3,
+ latitude: 30.571100,
+ longitude: 104.065200,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
+ width: 15,
+ height: 15,
+ anchor: { x: 0.5, y: 0.5 },
+ name: '中国南方电网·3号门'
+ },
+ {
+ id: 4,
+ latitude: 30.574100,
+ longitude: 104.066100,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
+ width: 15,
+ height: 15,
+ anchor: { x: 0.5, y: 0.5 },
+ name: '中国南方电网·4号门'
+ },
+ {
+ id: 5,
+ latitude: 30.570100,
+ longitude: 104.067100,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
+ width: 15,
+ height: 15,
+ anchor: { x: 0.5, y: 0.5 },
+ name: '中国南方电网·5号门'
+ }
+ ];
+ setMarkers(defaultMarkers);
+
+ getLocationInfo();
+ }, []);
+
+ // 规划路线
+ useEffect(() => {
+ const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
+ myAmapFun.getDrivingRoute({
+ origin: `${longitude},${latitude}`,
+ destination: `${endSite.longitude},${endSite.latitude}`,
+ success: function (data) {
+ const points = parseRouteToPoints(data);
+
+ setPolyline([{
+ points: points,
+ color: '#6D7FD9',
+ width: 10,
+ arrowLine: true,
+ }])
+
+ // 设置起点和终点标记
+ if (points.length > 0) {
+ const startPoint = points[0];
+ const endPoint = points[points.length - 1];
+
+ // 检查 storage 中的 startSite 是否存在
+ const s = Taro.getStorageSync('startSite');
+ if ([1, 2, 3, 4, 7, 8].includes(tripStatus)) {
+ const isCar = [4, 7].includes(tripStatus);
+
+ const startMarker: any = {
+ id: 1,
+ latitude: startPoint.latitude,
+ longitude: startPoint.longitude,
+ iconPath: isCar
+ ? 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e51e92e6-700d-4e83-b3e0-14e6f18e0b9a.png'
+ : 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/51e7cb75-72af-4994-9d6e-d999efb2fbc7.png',
+ width: isCar ? 40 : 22,
+ height: isCar ? 20 : 40,
+ anchor: { x: 0.5, y: 1 },
+ zIndex: 2,
+ };
+
+ if (tripStatus === 4) {
+ startMarker.name = '车辆位置';
+ startMarker.customCallout = { anchorY: 0, anchorX: 0, display: 'ALWAYS' };
+ } else if (tripStatus !== 7) {
+ startMarker.name = s?.name || '当前位置';
+ startMarker.customCallout = { anchorY: 8, anchorX: 0, display: 'ALWAYS' };
+ }
+
+ setMarkers([
+ // 起点指针
+ startMarker,
+ // 终点指针
+ {
+ id: 2,
+ latitude: endPoint.latitude,
+ longitude: endPoint.longitude,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/6a56b1eb-ec37-4839-8733-262942af070d.png',
+ width: 22,
+ height: 40,
+ anchor: { x: 0.5, y: 1 },
+ zIndex: 2,
+ name: tripStatus === 7 ? '已到达' : endSite.name,
+ customCallout: {
+ anchorY: 8,
+ anchorX: 0,
+ display: 'ALWAYS'
+ }
+ },
+ ])
+ }
+
+ // 调整视角以包含所有路线点
+ const mapCtx = Taro.createMapContext('map');
+ mapCtx.includePoints({
+ points: points,
+ padding: [50, 50, 300, 50]
+ })
+ }
+ },
+ fail: function (info) {
+ console.log('规划失败', info)
+ }
+ })
+ }, [tripStatus, endSite, longitude, latitude])
+
+ const formatTime = (sec: number) => {
+ const m = Math.floor(sec / 60);
+ const s = sec % 60;
+ return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
+ }
+
+ // 获取用户位置信息
+ const getLocationInfo = () => {
+ Taro.getLocation({ type: 'gcj02' })
+ .then(res => {
+ setLatitude(res.latitude);
+ setLongitude(res.longitude);
+ })
+ }
+
+ // 返回当前位置
+ const toOrigin = () => {
+ const mapCtx = Taro.createMapContext('map');
+ mapCtx.moveToLocation();
+ }
+
+ const handleCloseCustomerService = () => {
+ setShowCustomerService(false);
+ };
+
+ const openService = () => {
+ setShowCustomerService(true);
+ }
+
+
+ const toBack = () => {
+ if (tripStatus == 1) {
+
+ }
+ Taro.switchTab({
+ url: '/pages/robotaxi/home/index'
+ })
+ }
+
+ // 立即呼叫
+ const callImmediately = () => {
+ if (!endSite.name) {
+ Taro.showToast({
+ title: '请选择站点',
+ icon: 'none',
+ })
+ return
+ }
+ setTripStatus(2)
+ // 校验上车点是否存在
+ const s = Taro.getStorageSync('startSite');
+ if (s && s.latitude && s.longitude) {
+ // 移动地图中心到上车点
+ setLatitude(s.latitude);
+ setLongitude(s.longitude);
+ }
+ }
+
+ // 确认取消
+ const handleConfirmCancel = () => {
+ setShowCancelPopup(false);
+ setTripStatus(1);
+ }
+
+ // 关闭取消弹窗
+ const handleCloseCancelPopup = () => {
+ setShowCancelPopup(false);
+ }
+
+ // 打开取消弹窗
+ const handleOpenCancelPopup = () => {
+ setShowCancelPopup(true);
+ }
+
+ // 关闭安全注意事项弹窗
+ const handleCloseAnnouncementsPopup = () => {
+ setShowAnnouncementsPopup(false);
+ }
+
+ // 打开结束行程弹窗
+ const handleOpenOverRoutePopup = () => {
+ setShowOverRoutePopup(true);
+ }
+
+ // 关闭结束行程弹窗
+ const handleCloseOverRoutePopup = () => {
+ setShowOverRoutePopup(false);
+ }
+
+ // 确认结束行程
+ const handleConfirmOverRoute = () => {
+ setShowOverRoutePopup(false);
+ setTripStatus(8);
+ }
+
+ // 确认支付
+ const handlePay = () => {
+ setTripStatus(9);
+ }
+
+ // 关闭乘车需知弹窗
+ const handleClosePassengerNotice = () => {
+ setShowPassengerNotice(false);
+ }
+
+ return (
+ <View className="home-container">
+ <Map
+ id="map"
+ longitude={longitude}
+ latitude={latitude}
+ scale={14}
+ markers={markers}
+ polyline={polyline}
+ showLocation={!!longitude}
+ style={{ width: '100%', height: '100%' }}
+ >
+ {/* 自定义callout */}
+ {
+ ![2, 3].includes(tripStatus) && markers.map((item) => {
+ return (
+ <CoverView slot="callout" marker-id={item.id} key={item.id} className="custom-callout">
+ {
+ (!['车辆位置', '已到达'].includes(item.name)) &&
+ <CoverView className="custom-callout-content">
+ {item.name} <CoverImage className="custom-callout-icon" src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/f9e285fc-4c09-404d-90f8-4bac178565d0.png' />
+ </CoverView>
+ }
+ {
+ item.name === '车辆位置' &&
+ <CoverView className="custom-callout-content">
+ <Text className="car-time"><Text className="car-time-num">3</Text>公里<Text className="car-time-nuum">2</Text>分钟</Text>
+ </CoverView>
+ }
+ {
+ item.name === '已到达' &&
+ <CoverView className="custom-callout-content">
+ 已到达
+ </CoverView>
+ }
+ </CoverView>
+ )
+ })
+ }
+ {
+ (tripStatus === 2 || tripStatus === 3) && markers.map((item) => {
+ if (item.id === 1) {
+ return (
+ <CoverView slot="callout" marker-id={item.id} key={item.id} className="waiting-callout">
+ {tripStatus === 3 && (
+ <CoverView className="waiting-radiation-box">
+ <CoverView className="waiting-radiation-ring"></CoverView>
+ <CoverView className="waiting-radiation-ring"></CoverView>
+ </CoverView>
+ )}
+ <CoverView className="waiting-callout-bg">
+ <CoverImage className="waiting-callout-bg-img" src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/f55ce277-68de-4615-9c7c-807f588afaac.png" />
+ <CoverView className="waiting-callout-bg-text">{formatTime(elapsedSeconds)}</CoverView>
+ </CoverView>
+ <CoverView className="waiting-callout-txt">
+ <CoverView className="waiting-callout-txt-content">{item.name}</CoverView>
+ </CoverView>
+ </CoverView>
+ )
+ }
+ return null
+ })
+ }
+ {/* 选择城市 */}
+ <View onClick={toBack} className="select-city">
+ <Image className="select-city-icon" src={backIcon} />
+ </View>
+ {/* 地图浮层入口 */}
+ <View className="map-entry">
+ <Image onClick={openService} className="map-entry-icon" src={contactService} />
+ <View className="split-line"></View>
+ <Image className="map-entry-icon" src={coupon} />
+ <View className="split-line"></View>
+ <Image className="map-entry-icon" src={suggest} />
+ </View>
+ <View className="container">
+ <View className="container-inner">
+ {/* 复位按钮 */}
+ <View className="origin-box">
+ <Image onClick={toOrigin} className="origin-icon" src={originIcon} />
+ </View>
+ <View catchMove onClick={(e) => e.stopPropagation()}>
+ {/* 发布路线 */}
+ {
+ tripStatus == 1 && <ReleaseRoute callImmediately={callImmediately} />
+ }
+ {/* 发布中弹窗 */}
+ {
+ tripStatus == 2 && <ReleaseBeBeing handleOpenCancelPopup={handleOpenCancelPopup} onFinish={() => setTripStatus(3)} />
+ }
+ {/* 呼叫车辆弹窗 */}
+ {
+ tripStatus >= 3 &&
+ <CallingCar
+ key={tripStatus}
+ tripStatus={tripStatus}
+ handleOpenCancelPopup={handleOpenCancelPopup}
+ handleOpenOverRoutePopup={handleOpenOverRoutePopup}
+ handlePay={handlePay}
+ updateStatus={() => setTripStatus(prev => prev + 1)}
+ />
+ }
+ </View>
+ </View>
+ </View>
+ </Map>
+ {/* 客服弹窗 */}
+ <CustomerServicePopup
+ show={showCustomerService}
+ onClose={handleCloseCustomerService}
+ />
+ {/* 取消弹窗 */}
+ <CancelPopup
+ show={showCancelPopup}
+ cancelType={cancelType}
+ onClose={handleCloseCancelPopup}
+ onConfirm={handleConfirmCancel}
+ />
+ {/* 安全注意事项弹窗 */}
+ <AnnouncementsPopup
+ show={showAnnouncementsPopup}
+ onClose={handleCloseAnnouncementsPopup}
+ />
+ {/* 结束行程弹窗 */}
+ <OverRoute
+ show={showOverRoutePopup}
+ onClose={handleCloseOverRoutePopup}
+ onConfirm={handleConfirmOverRoute}
+ />
+ {/* 乘车需知弹窗 */}
+ <PassengerNotice
+ show={showPassengerNotice}
+ onClose={handleClosePassengerNotice}
+ />
+ </View>
+ )
+}
diff --git a/src/robotaxi/pages/select-map-site/components/cancel-popup.tsx b/src/robotaxi/pages/select-map-site/components/cancel-popup.tsx
deleted file mode 100644
index 53c5a36..0000000
--- a/src/robotaxi/pages/select-map-site/components/cancel-popup.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import Taro from '@tarojs/taro';
-import { View, Text, Image } from '@tarojs/components';
-import { useEffect, useState } from 'react';
-import './css-folder/cancel-popup.less'
-
-type Props = {
- show?: boolean;
- onClose?: () => void;
- onConfirm?: () => void;
-}
-
-export default function CancelPopup({ show = false, onClose, onConfirm }: Props) {
- const [visible, setVisible] = useState(false);
- const [animationData, setAnimationData] = useState<any>();
-
- useEffect(() => {
- if (show && !visible) {
- setVisible(true);
- const animation = Taro.createAnimation({
- duration: 300,
- timingFunction: 'ease-out'
- });
- animation.translateY(0).step();
- setAnimationData(animation.export());
- } else if (!show && visible) {
- const animation = Taro.createAnimation({
- duration: 300,
- timingFunction: 'ease-in'
- });
- animation.translateY(500).step();
- setAnimationData(animation.export());
-
- setTimeout(() => {
- setVisible(false);
- }, 300);
- }
- }, [show]);
-
- if (!visible) return null;
-
- return (
- <View className='cancel-popup' catchMove>
- <View className='cancel-mask'></View>
- <View className='cancel-content' animation={animationData}>
- <View className='cancel-box'>
- <Image onClick={onClose} className='cancel-icon' src={require('../../../assets/close-icon.png')} />
- </View>
- <View className='cancel-tip-box'>
- <View className='cancel-tip-box-title'>2分钟内预计有车,确定取消吗?</View>
- <View className='cancel-tip-box-desc'>本单限时优惠16.2元,重新叫车可能等待更久</View>
- </View>
- <View className='cancel-btn-box'>
- <View className='cancel-btn-cancel' onClick={onConfirm}>确认取消</View>
- <View className='cancel-btn-again' onClick={onClose}>再等等</View>
- </View>
- </View>
- </View>
- )
-}
\ No newline at end of file
diff --git a/src/robotaxi/pages/select-map-site/components/tip-popup.tsx b/src/robotaxi/pages/select-map-site/components/tip-popup.tsx
index f52ef3e..08ae1a6 100644
--- a/src/robotaxi/pages/select-map-site/components/tip-popup.tsx
+++ b/src/robotaxi/pages/select-map-site/components/tip-popup.tsx
@@ -41,7 +41,7 @@
<View className='tip-popup' catchMove>
<View className='tip-mask'></View>
<View className='tip-content' animation={animationData}>
- <Image className='close-icon' src={require('../../../assets/close-icon.png')} />
+ <Image onClick={onClose} className='close-icon' src={require('../../../assets/close-icon.png')} />
<View className='tip-title'>{tipType === '1' ? '上车点与目的地距离较近,建议步行前往' : '当前位置距上车点较远,暂不支持叫车,请重新选择上车点'}</View>
<View onClick={onClose} className='reselect-btn'>重新选点</View>
</View>
diff --git a/src/robotaxi/pages/select-map-site/index.less b/src/robotaxi/pages/select-map-site/index.less
index 5a6cf04..71de916 100644
--- a/src/robotaxi/pages/select-map-site/index.less
+++ b/src/robotaxi/pages/select-map-site/index.less
@@ -116,18 +116,6 @@
}
}
-@keyframes radiate {
- 0% {
- transform: translate(-50%, -50%) scale(1);
- opacity: 1;
- }
-
- 100% {
- transform: translate(-50%, -50%) scale(25);
- opacity: 0;
- }
-}
-
@keyframes pointer-bounce {
0% {
transform: translateY(0);
@@ -266,145 +254,3 @@
}
}
}
-
-.custom-callout {
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.custom-callout-content {
- display: flex;
- align-items: center;
- border-radius: 16px;
- background: rgba(255, 255, 255, 0.9);
- padding: 12px 12px 12px 20px;
- font-size: 24px;
- font-weight: bold;
- color: rgba(0, 0, 0, 0.9);
- line-height: 48px;
- box-shadow: 0px 4px 16px 0px rgba(45, 48, 64, 0.2);
-}
-
-.custom-callout-icon {
- margin-left: 8px;
- width: 32px;
- height: 32px;
- flex-shrink: 0;
- display: block;
-}
-
-.waiting-callout {
- position: relative;
- z-index: 10;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: center;
- overflow: visible; // 确保辐射动画不被裁剪
-}
-
-.waiting-radiation-box {
- position: absolute;
- top: 100%; // 移到气泡下方
- left: 50%;
- margin-top: 30px; // 向下偏移以对齐Marker锚点(预估值)
- width: 0;
- height: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- pointer-events: none;
- overflow: visible;
-}
-
-@keyframes ripple {
- 0% {
- opacity: 0.6;
- transform: translate(-50%, -50%) scale(0.2);
- }
-
- 100% {
- opacity: 0;
- transform: translate(-50%, -50%) scale(2.5);
- }
-}
-
-.waiting-radiation-ring {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 200px;
- height: 200px;
- border-radius: 50%;
- background: rgba(26, 79, 255, 0.05);
- border: 1px solid rgba(26, 79, 255, 0.2);
- opacity: 0; // 初始不可见,由动画控制
- animation: ripple 3.5s infinite linear;
-
- &:nth-child(1) {
- animation-delay: 0s;
- }
-
- &:nth-child(2) {
- animation-delay: 1s;
- }
-}
-
-
-.waiting-callout-bg {
- position: relative;
- height: 70px;
- min-width: 155px;
- text-align: center;
- width: fit-content;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.waiting-callout-bg-img {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 1;
-}
-
-.waiting-callout-bg-text {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 100;
- font-size: 22px;
- font-weight: 500;
- letter-spacing: normal;
- color: #FFFFFF;
- line-height: 48px;
- padding: 0 20px;
- box-sizing: border-box;
-}
-
-.waiting-callout-txt {
- position: relative;
- z-index: 200;
- margin-top: -26px;
- display: flex;
- align-items: center;
- border-radius: 16px;
- background: rgba(255, 255, 255, 0.9);
- padding: 12px 12px 12px 20px;
- min-width: 155px;
-}
-
-.waiting-callout-txt-content {
- font-size: 24px;
- font-weight: bold;
- letter-spacing: normal;
- color: rgba(0, 0, 0, 0.9);
- line-height: 48px;
-}
\ No newline at end of file
diff --git a/src/robotaxi/pages/select-map-site/index.tsx b/src/robotaxi/pages/select-map-site/index.tsx
index 8429f35..4986bc3 100644
--- a/src/robotaxi/pages/select-map-site/index.tsx
+++ b/src/robotaxi/pages/select-map-site/index.tsx
@@ -1,16 +1,10 @@
import Taro from "@tarojs/taro"
import { useEffect, useState } from 'react';
-import { View, Map, Text, Image, CoverView, CoverImage } from '@tarojs/components';
+import { View, Map, Text, Image } from '@tarojs/components';
import './index.less';
import CustomerServicePopup from '../../components/customer-service';
-import ReleaseRoute from './components/release-route';
import TipPopup from './components/tip-popup';
-import ReleaseBeBeing from './components/release-be-being';
-import CancelPopup from './components/cancel-popup';
-import CallingCar from './components/calling-car';
-import AnnouncementsPopup from './components/announcements-popup';
-import OverRoute from './components/over-route';
import backIcon from '../../../robotaxi/assets/back-icon.png'
import contactService from '../../../robotaxi/assets/contact-service.png'
@@ -19,9 +13,7 @@
import originIcon from '../../../robotaxi/assets/origin-icon.png'
import pointer from '../../../robotaxi/assets/pointer.png'
import rightIcon from '../../../robotaxi/assets/right-icon.png'
-import { findNearestMarker, parseRouteToPoints, getDistance } from '../../../utils/Robotaxi-public-method';
-import amapFile from '../../../libs/amap-wx.130';
-import { AMAP_KEY } from '../../../constants/api';
+import { findNearestMarker, getDistance } from '../../../utils/Robotaxi-public-method';
export default function SelectMapSite() {
@@ -31,23 +23,13 @@
const [showToolTip, setShowToolTip] = useState(true); // 提示是否显示
const [bounce, setBounce] = useState(false); // 是否弹跳
const [markers, setMarkers] = useState<any[]>([]); // 标记点列表
- const [polyline, setPolyline] = useState<any[]>([]); // 路线
const [pageType, setPageType] = useState('end'); // 起点start 终点end
const [selectedSite, setSelectedSite] = useState<any>({}); // 选择的站点
- const [tripStatus, setTripStatus] = useState(0); // 行程状态 0:默认状态 1:发布行程 2:发布中 3:呼叫车中 4:车辆即将到达 5:车辆到达上车点 6:车辆行驶中 7:到达目的地 8:行程已结束 9:支付成功
const [showTipPopup, setShowTipPopup] = useState(false); // 提示弹窗是否显示
const [tipType, setTipType] = useState(''); // 提示类型 1:上下车点近 2:距上车点远
const [startSite, setStartSite] = useState<any>(null); // 上车点
- const [elapsedSeconds, setElapsedSeconds] = useState(0); // 等待计时秒数
- const [showCancelPopup, setShowCancelPopup] = useState(false); // 取消弹窗是否显示
- const [showAnnouncementsPopup, setShowAnnouncementsPopup] = useState(false); // 安全注意事项动画弹窗是否显示
- const [showOverRoutePopup, setShowOverRoutePopup] = useState(false); // 结束行程弹窗是否显示
const router = Taro.useRouter();
-
- useEffect(() => {
- Taro.setStorageSync('startSite', startSite);
- }, [tripStatus]);
// 校验上车点是否存在
useEffect(() => {
@@ -56,38 +38,6 @@
setStartSite(s);
}
}, []);
-
- // 监听行程状态,处理等待计时
- useEffect(() => {
- let timer: any = null;
- if (tripStatus === 2 || tripStatus === 3) {
- // 状态为2或3时,将地图中心定位到上车点
- const s = Taro.getStorageSync('startSite');
- if (s && s.latitude && s.longitude) {
- setLatitude(s.latitude);
- setLongitude(s.longitude);
-
- // 增加延迟,确保地图上下文就绪
- setTimeout(() => {
- const mapCtx = Taro.createMapContext('map');
- mapCtx.moveToLocation({
- latitude: s.latitude,
- longitude: s.longitude
- });
- }, 500);
- }
-
- setElapsedSeconds(0);
- timer = setInterval(() => {
- setElapsedSeconds(prev => prev + 1);
- }, 1000);
- } else {
- setElapsedSeconds(0);
- }
- return () => {
- if (timer) clearInterval(timer);
- };
- }, [tripStatus]);
// 页面初始化
useEffect(() => {
@@ -146,97 +96,22 @@
height: 15,
anchor: { x: 0.5, y: 0.5 },
name: '中国南方电网·5号门'
+ },
+ {
+ id: 6,
+ latitude: 30.584992,
+ longitude: 104.054063,
+ iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
+ width: 15,
+ height: 15,
+ anchor: { x: 0.5, y: 0.5 },
+ name: '冯校长老火锅'
}
];
setMarkers(defaultMarkers);
getLocationInfo();
}, []);
-
- // 规划路线
- useEffect(() => {
- if (tripStatus === 1 && pageType === 'end' && selectedSite.longitude && longitude) {
- const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
- myAmapFun.getDrivingRoute({
- origin: `${longitude},${latitude}`,
- destination: `${selectedSite.longitude},${selectedSite.latitude}`,
- success: function (data) {
- const points = parseRouteToPoints(data);
-
- setPolyline([{
- points: points,
- color: '#6D7FD9',
- width: 10,
- arrowLine: true,
- }])
-
- // 设置起点和终点标记
- if (points.length > 0) {
- const startPoint = points[0];
- const endPoint = points[points.length - 1];
-
- // 检查 storage 中的 startSite 是否存在
- const s = Taro.getStorageSync('startSite');
-
- setMarkers([
- // 起点指针
- {
- id: 1,
- latitude: startPoint.latitude,
- longitude: startPoint.longitude,
- iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/51e7cb75-72af-4994-9d6e-d999efb2fbc7.png',
- width: 22,
- height: 40,
- anchor: { x: 0.5, y: 1 },
- zIndex: 2,
- name: s?.name || '当前位置',
- customCallout: {
- anchorY: 8,
- anchorX: 0,
- display: 'ALWAYS'
- }
- },
- // 终点指针
- {
- id: 2,
- latitude: endPoint.latitude,
- longitude: endPoint.longitude,
- iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/6a56b1eb-ec37-4839-8733-262942af070d.png',
- width: 22,
- height: 40,
- anchor: { x: 0.5, y: 1 },
- zIndex: 2,
- name: selectedSite.name,
- customCallout: {
- anchorY: 8,
- anchorX: 0,
- display: 'ALWAYS'
- }
- },
- ])
-
- // 调整视角以包含所有路线点
- const mapCtx = Taro.createMapContext('map');
- mapCtx.includePoints({
- points: points,
- padding: [50, 50, 300, 50]
- })
- }
- },
- fail: function (info) {
- console.log('规划失败', info)
- }
- })
- } else if (tripStatus !== 2 && tripStatus !== 3) {
- setPolyline([])
- }
- }, [tripStatus, pageType, selectedSite, longitude, latitude])
-
- const formatTime = (sec: number) => {
- const m = Math.floor(sec / 60);
- const s = sec % 60;
- return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
- }
// 获取用户位置信息
const getLocationInfo = () => {
@@ -255,7 +130,6 @@
// 地图区域变化时触发,判断是否吸附到最近的站点停靠点
const handleRegionChange = (e: any) => {
- if (tripStatus === 1 || tripStatus === 2) return
const type = e?.type || e?.detail?.type;
// 获取触发源: gesture (手势), update (接口调用)
const causedBy = e?.causedBy || e?.detail?.causedBy;
@@ -306,15 +180,9 @@
const toBack = () => {
- if (tripStatus == 0) {
- Taro.navigateBack({
- delta: 1,
- })
- } else {
- Taro.switchTab({
- url: '/pages/robotaxi/home/index'
- })
- }
+ Taro.navigateBack({
+ delta: 1,
+ })
}
const confirmSite = () => {
@@ -355,31 +223,15 @@
return;
}
}
- setTripStatus(1)
- }
- }
- // 立即呼叫
- const callImmediately = () => {
- if (!selectedSite.name) {
- Taro.showToast({
- title: '请选择站点',
- icon: 'none',
+ Taro.setStorageSync('endSite', {
+ latitude: selectedSite.latitude,
+ longitude: selectedSite.longitude,
+ name: selectedSite.name,
})
- return
- }
- setTripStatus(2)
- // 校验上车点是否存在
- const s = Taro.getStorageSync('startSite');
- if (s && s.latitude && s.longitude) {
- setStartSite(s);
-
- // 移动地图中心到上车点
- setLatitude(s.latitude);
- setLongitude(s.longitude);
-
- // 确保显示 Tooltip
- setShowToolTip(true);
+ Taro.reLaunch({
+ url: '/robotaxi/pages/begin-route/index',
+ })
}
}
@@ -394,48 +246,6 @@
setSelectedSite({ name: item })
}
- // 确认取消
- const handleConfirmCancel = () => {
- setShowCancelPopup(false);
- setTripStatus(0);
- }
-
- // 关闭取消弹窗
- const handleCloseCancelPopup = () => {
- setShowCancelPopup(false);
- }
-
- // 打开取消弹窗
- const handleOpenCancelPopup = () => {
- setShowCancelPopup(true);
- }
-
- // 关闭安全注意事项弹窗
- const handleCloseAnnouncementsPopup = () => {
- setShowAnnouncementsPopup(false);
- }
-
- // 打开结束行程弹窗
- const handleOpenOverRoutePopup = () => {
- setShowOverRoutePopup(true);
- }
-
- // 关闭结束行程弹窗
- const handleCloseOverRoutePopup = () => {
- setShowOverRoutePopup(false);
- }
-
- // 确认结束行程
- const handleConfirmOverRoute = () => {
- setShowOverRoutePopup(false);
- setTripStatus(8);
- }
-
- // 确认支付
- const handlePay = () => {
- setTripStatus(9);
- }
-
return (
<View className="home-container">
<Map
@@ -444,47 +254,10 @@
latitude={latitude}
scale={14}
markers={markers}
- polyline={polyline}
showLocation={!!longitude}
onRegionChange={handleRegionChange}
style={{ width: '100%', height: '100%' }}
>
- {/* 自定义callout */}
- {
- tripStatus === 1 && markers.map((item) => {
- return (
- <CoverView slot="callout" marker-id={item.id} key={item.id} className="custom-callout">
- <CoverView className="custom-callout-content">
- {item.name} <CoverImage className="custom-callout-icon" src='https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/f9e285fc-4c09-404d-90f8-4bac178565d0.png' />
- </CoverView>
- </CoverView>
- )
- })
- }
- {
- (tripStatus === 2 || tripStatus === 3) && markers.map((item) => {
- if (item.id === 1) {
- return (
- <CoverView slot="callout" marker-id={item.id} key={item.id} className="waiting-callout">
- {tripStatus === 3 && (
- <CoverView className="waiting-radiation-box">
- <CoverView className="waiting-radiation-ring"></CoverView>
- <CoverView className="waiting-radiation-ring"></CoverView>
- </CoverView>
- )}
- <CoverView className="waiting-callout-bg">
- <CoverImage className="waiting-callout-bg-img" src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/f55ce277-68de-4615-9c7c-807f588afaac.png" />
- <CoverView className="waiting-callout-bg-text">{formatTime(elapsedSeconds)}</CoverView>
- </CoverView>
- <CoverView className="waiting-callout-txt">
- <CoverView className="waiting-callout-txt-content">{item.name}</CoverView>
- </CoverView>
- </CoverView>
- )
- }
- return null
- })
- }
{/* 选择城市 */}
<View onClick={toBack} className="select-city">
<Image className="select-city-icon" src={backIcon} />
@@ -498,25 +271,23 @@
<Image className="map-entry-icon" src={suggest} />
</View>
{/* 指针 */}
- {
- tripStatus === 0 && <View className="pointer-box">
- {(showToolTip && selectedSite.name) && (
- <View className="toolTip">
- <View className="toolTip-bg">
- <Text className="toolTip-bg-text">'限停3分钟'</Text>
- </View>
- <View className="toolTip-txt">
- <View className="toolTip-txt-content">{selectedSite.name}</View>
- <Image className="toolTip-icon" src={rightIcon} />
- </View>
+ <View className="pointer-box">
+ {(showToolTip && selectedSite.name) && (
+ <View className="toolTip">
+ <View className="toolTip-bg">
+ <Text className="toolTip-bg-text">'限停3分钟'</Text>
</View>
- )}
- <Image
- className={`pointer-icon ${bounce ? 'bounce' : ''}`}
- src={pointer}
- />
- </View>
- }
+ <View className="toolTip-txt">
+ <View className="toolTip-txt-content">{selectedSite.name}</View>
+ <Image className="toolTip-icon" src={rightIcon} />
+ </View>
+ </View>
+ )}
+ <Image
+ className={`pointer-icon ${bounce ? 'bounce' : ''}`}
+ src={pointer}
+ />
+ </View>
<View className="container">
<View className="container-inner">
{/* 复位按钮 */}
@@ -526,7 +297,7 @@
<View catchMove onClick={(e) => e.stopPropagation()}>
{/* 确认站点 */}
{
- (tripStatus == 0 && selectedSite.name) && <View className="content-box">
+ selectedSite.name && <View className="content-box">
<View className="content-limit-bg"></View>
<View className="content-title">{pageType === 'start' ? '确认起点' : '确认目的地'}</View>
<View className="limit-tip">目的地附近道路有<Text className="emphasis-txt">通行限制</Text>,为您推荐适合的下车位置</View>
@@ -540,26 +311,6 @@
</View>
<View onClick={confirmSite} className="site-confirm">{pageType === 'start' ? '确认起点站点' : '确认目的地站点'}</View>
</View>
- }
- {/* 发布路线 */}
- {
- tripStatus == 1 && <ReleaseRoute callImmediately={callImmediately} />
- }
- {/* 发布中弹窗 */}
- {
- tripStatus == 2 && <ReleaseBeBeing handleOpenCancelPopup={handleOpenCancelPopup} onFinish={() => setTripStatus(3)} />
- }
- {/* 呼叫车辆弹窗 */}
- {
- tripStatus >= 3 &&
- <CallingCar
- key={tripStatus}
- tripStatus={tripStatus}
- handleOpenCancelPopup={handleOpenCancelPopup}
- handleOpenOverRoutePopup={handleOpenOverRoutePopup}
- handlePay={handlePay}
- updateStatus={() => setTripStatus(prev => prev + 1)}
- />
}
</View>
</View>
@@ -575,23 +326,6 @@
show={showTipPopup}
onClose={handleCloseTipPopup}
tipType={tipType}
- />
- {/* 取消弹窗 */}
- <CancelPopup
- show={showCancelPopup}
- onClose={handleCloseCancelPopup}
- onConfirm={handleConfirmCancel}
- />
- {/* 安全注意事项弹窗 */}
- <AnnouncementsPopup
- show={showAnnouncementsPopup}
- onClose={handleCloseAnnouncementsPopup}
- />
- {/* 结束行程弹窗 */}
- <OverRoute
- show={showOverRoutePopup}
- onClose={handleCloseOverRoutePopup}
- onConfirm={handleConfirmOverRoute}
/>
</View>
)
diff --git a/src/robotaxi/pages/site-search/index.tsx b/src/robotaxi/pages/site-search/index.tsx
index be5ce9f..9990f26 100644
--- a/src/robotaxi/pages/site-search/index.tsx
+++ b/src/robotaxi/pages/site-search/index.tsx
@@ -128,11 +128,21 @@
}
]) // 非运营区域站点
const [searchValue, setSearchValue] = useState('') //搜索值
- const [pageType, setPageType] = useState('end') //出发地 start 目的地 end
+ const [pageType, setPageType] = useState('') //出发地 start 目的地 end
const [showAllHistory, setShowAllHistory] = useState(false) // 是否展示全部历史记录
const [showAllRecommend, setShowAllRecommend] = useState(false) // 是否展示全部推荐站点
const MAX_SHOW_COUNT = 5 // 默认最多显示5条
+
+ const router = Taro.useRouter();
+
+ useEffect(() => {
+ console.log(router.params.pageType);
+
+ if (router.params.pageType) {
+ setPageType(router.params.pageType);
+ }
+ }, [])
const searchSite = () => {
console.log(searchValue)
@@ -169,11 +179,11 @@
}
-const toSelectSite = () => {
- Taro.navigateTo({
- url: '/robotaxi/pages/select-map-site/index?pageType=' + pageType,
- })
-}
+ const toSelectSite = () => {
+ Taro.navigateTo({
+ url: '/robotaxi/pages/select-map-site/index?pageType=' + pageType,
+ })
+ }
return (
--
Gitblit v1.7.1