| src/robotaxi/api/begin-route.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/api/feedback.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/api/order.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/components/css-folder/evaluate-popup.less | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/begin-route/components/evaluate-popup.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/expense-description/index.less | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/robotaxi/pages/expense-description/index.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/robotaxi/api/begin-route.ts
@@ -286,7 +286,8 @@ return request({ url: _ROBOTAXI_HOST + `/order/api/v1/mini-program/order/comment`, method: 'POST', payload payload, showToast: false }, true); } src/robotaxi/api/feedback.ts
@@ -1,9 +1,21 @@ import request from '../../utils/request'; import { _ROBOTAXI_HOST } from '../../constants/api'; import type { SubmitComplaintPayload, SubmitFeedbackResponse } from './types/begin-route'; import type { SubmitComplaintPayload, SubmitFeedbackResponse, SubmitOrderCommentPayload } from './types/begin-route'; import type { FeedbackTypeItem } from './types/feedback'; // 提交订单投诉 export const submitOrderComplaint = async ( payload: SubmitOrderCommentPayload ): Promise<SubmitFeedbackResponse> => { return request({ url: _ROBOTAXI_HOST + `/operation/base/v1/mini-program/orderComplaint/submit`, method: 'POST', payload, showToast: false }, true); } // 提交独立反馈投诉 export const submitComplaint = async ( payload: SubmitComplaintPayload ): Promise<SubmitFeedbackResponse> => { src/robotaxi/api/order.ts
@@ -53,6 +53,7 @@ start: FareItem[]; time: number; payMoney?: number; // 实际支付金额,虽然detail.txt没写,但页面展示需要,可能包含在返回中或需计算 hasComplaint: boolean; // 是否已反馈 } export type FareBreakdownPayload = { src/robotaxi/pages/begin-route/components/css-folder/evaluate-popup.less
@@ -25,6 +25,11 @@ border-radius: 60px 60px 0 0; padding: 64px 0 0 0; &.is-fare-feedback { height: 812px; padding-top: 48px; } .evaluate-rating-switcher, .content-box { transition: opacity 180ms ease, transform 180ms ease; @@ -84,6 +89,11 @@ align-self: stretch; padding-left: 30px; box-sizing: border-box; &.is-centered { text-align: center; padding-left: 0; } } &-content { src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react'; import './css-folder/evaluate-popup.less' import { getOrderCommentType, submitOrderComment } from '../../../api/begin-route'; import { submitComplaint } from '../../../api/feedback'; import { submitOrderComplaint } from '../../../api/feedback'; import { _ROBOTAXI_HOST } from '../../../../constants/api'; import useImageUpload from '../../../hooks/use-image-upload'; import { getOrderId } from '../../../utils/order'; @@ -16,7 +16,7 @@ onConfirm?: () => void; orderId?: string; orderRating?: number; submitMode?: 'comment' | 'complaint'; submitMode?: 'comment' | 'orderComplaint'; /** * 是否隐藏问题类型选择 */ @@ -25,6 +25,11 @@ * 是否禁止切换满意度评分(点击图标/左右滑动均禁用),固定为传入的 orderRating */ disableRatingSwitch?: boolean; /** * 是否为「订单车费反馈」入口(车费说明页 complaint-btn 触发): * 标题居中展示为「订单车费反馈」,隐藏满意度切换区(图标+文案) */ isFareFeedback?: boolean; } export default function EvaluatePopup({ @@ -35,7 +40,8 @@ orderRating, submitMode = 'comment', hideFeedbackType = false, disableRatingSwitch = false disableRatingSwitch = false, isFareFeedback = false }: Props) { const bad = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/bad.png'; const normal = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/normal.png'; @@ -67,6 +73,8 @@ uploadUrl: _ROBOTAXI_HOST + '/operation/base/v1/mini-program/oss/upload' }); // 图片上传状态与操作 const toastCommomText = submitMode === 'orderComplaint' ? '反馈' : '评价'; const currentOrderRating = Number(selectedRating) || 1; // 根据当前选择的评分,动态展示评价弹窗的文案 const popupDisplayConfig = { @@ -97,7 +105,7 @@ }; const evaluateDimension = evaluateDimensionMap[currentOrderRating] || 3; // 接口维度:1 满意,2 一般,3 不满意 const shouldRequireFeedbackType = !hideFeedbackType; const shouldHideFeedbackTextarea = submitMode === 'comment' && [2, 3].includes(currentOrderRating); const shouldHideFeedbackTextarea = [2, 3].includes(currentOrderRating); const hasFeedbackContent = !!feedbackContent.trim(); const canSubmit = shouldRequireFeedbackType ? selectedTypeIds.length > 0 && (shouldHideFeedbackTextarea || hasFeedbackContent) @@ -210,7 +218,7 @@ const targetOrderId = getOrderId(orderId); if (!targetOrderId) { showToast({ title: '订单信息缺失,无法提交评价', title: `订单信息缺失,无法提交${toastCommomText}`, icon: 'none' }); return; @@ -230,7 +238,7 @@ return; } isSubmittingRef.current = true; const submitter = submitMode === 'complaint' ? submitComplaint : submitOrderComment; const submitter = submitMode === 'orderComplaint' ? submitOrderComplaint : submitOrderComment; const picturePayload = JSON.stringify( pictures .filter(p => p.status === 'success' && p.server && p.server.id && p.server.url) @@ -249,20 +257,19 @@ const isFailure = res?.success === false || (typeof res?.code === 'number' && res.code !== 0); if (isFailure) { showToast({ title: '评价提交失败,请稍后重试', title: `${toastCommomText}提交失败,请稍后重试`, icon: 'none' }); return; } const successTitle = submitMode === 'complaint' ? '投诉已提交' : '评价已提交'; showToast({ title: successTitle, title: `${toastCommomText}已提交`, icon: 'none' }); onConfirm?.(); }).catch(() => { showToast({ title: '评价提交失败,请稍后重试', title: `${toastCommomText}提交失败,请稍后重试`, icon: 'none' }); }).finally(() => { @@ -330,7 +337,7 @@ <View className='evaluate-popup' catchMove onClick={(e) => e.stopPropagation()}> <View className='evaluate-mask' onClick={onClose}></View> <View className={`evaluate-content ${isRatingSwitching ? 'is-rating-switching' : ''}`} className={`evaluate-content ${isRatingSwitching ? 'is-rating-switching' : ''} ${isFareFeedback ? 'is-fare-feedback' : ''}`} animation={animationData} onTouchStart={handleRatingTouchStart} onTouchMove={handleRatingTouchMove} @@ -340,33 +347,37 @@ <Image onClick={onClose} src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/v2/close-fill-20260726.svg" /> </View> <View className='evaluate-rating-switcher'> <View className='evaluate-rating-switcher-title'>您对本次行程满意吗?</View> <View className={`evaluate-rating-switcher-content ${disableRatingSwitch ? 'is-single' : ''}`}> {(!disableRatingSwitch || selectedRating === 1) && ( <View className={`evaluate-rating-switcher-content-item ${disableRatingSwitch ? 'is-disabled' : ''}`} onClick={() => handleSelectRating(1)}> <Image src={selectedRating === 1 ? activeBad : bad} className='evaluate-rating-switcher-content-item-img' /> <Text className={`evaluate-rating-switcher-content-item-text ${selectedRating === 1 ? 'active-text' : ''}`}>不满意</Text> <View className={`evaluate-rating-switcher-title ${isFareFeedback ? 'is-centered' : ''}`}>{isFareFeedback ? '订单车费反馈' : '您对本次行程满意吗?'}</View> {!isFareFeedback && ( <> <View className={`evaluate-rating-switcher-content ${disableRatingSwitch ? 'is-single' : ''}`}> {(!disableRatingSwitch || selectedRating === 1) && ( <View className={`evaluate-rating-switcher-content-item ${disableRatingSwitch ? 'is-disabled' : ''}`} onClick={() => handleSelectRating(1)}> <Image src={selectedRating === 1 ? activeBad : bad} className='evaluate-rating-switcher-content-item-img' /> <Text className={`evaluate-rating-switcher-content-item-text ${selectedRating === 1 ? 'active-text' : ''}`}>不满意</Text> </View> )} {(!disableRatingSwitch || selectedRating === 2) && ( <View className={`evaluate-rating-switcher-content-item ${disableRatingSwitch ? 'is-disabled' : ''}`} onClick={() => handleSelectRating(2)}> <Image src={selectedRating === 2 ? activeNormal : normal} className='evaluate-rating-switcher-content-item-img' /> <Text className={`evaluate-rating-switcher-content-item-text ${selectedRating === 2 ? 'active-text' : ''}`}>一般般</Text> </View> )} {(!disableRatingSwitch || selectedRating === 3) && ( <View className={`evaluate-rating-switcher-content-item ${disableRatingSwitch ? 'is-disabled' : ''}`} onClick={() => handleSelectRating(3)}> <Image src={selectedRating === 3 ? activeGood : good} className='evaluate-rating-switcher-content-item-img' /> <Text className={`evaluate-rating-switcher-content-item-text ${selectedRating === 3 ? 'active-text' : ''}`}>很满意</Text> </View> )} </View> )} {(!disableRatingSwitch || selectedRating === 2) && ( <View className={`evaluate-rating-switcher-content-item ${disableRatingSwitch ? 'is-disabled' : ''}`} onClick={() => handleSelectRating(2)}> <Image src={selectedRating === 2 ? activeNormal : normal} className='evaluate-rating-switcher-content-item-img' /> <Text className={`evaluate-rating-switcher-content-item-text ${selectedRating === 2 ? 'active-text' : ''}`}>一般般</Text> </View> )} {(!disableRatingSwitch || selectedRating === 3) && ( <View className={`evaluate-rating-switcher-content-item ${disableRatingSwitch ? 'is-disabled' : ''}`} onClick={() => handleSelectRating(3)}> <Image src={selectedRating === 3 ? activeGood : good} className='evaluate-rating-switcher-content-item-img' /> <Text className={`evaluate-rating-switcher-content-item-text ${selectedRating === 3 ? 'active-text' : ''}`}>很满意</Text> </View> )} </View> <View className='evaluate-rating-switcher-summary'>{popupDisplayConfig.summary}</View> <View className='evaluate-rating-switcher-summary'>{popupDisplayConfig.summary}</View> </> )} </View> <View className='content-box'> {!hideFeedbackType && ( <View className='evaluate-problem-type'> <View className='evaluate-problem-type-content'> {typeOptions.map((item) => ( <View key={item?.id} className={`evaluate-problem-type-content-item ${selectedTypeIds.includes(normalizeFeedbackTypeId(item?.id)) && 'active-tag'}`} onClick={() => toggleFeedbackType(item?.id)}> src/robotaxi/pages/expense-description/index.less
@@ -1,12 +1,14 @@ @import '../../style/ui-variables.less'; .expense-detail-page { background: #F2F3F7; background: @gary-theme; min-height: 100vh; display: flex; flex-direction: column; padding: 32px 24px; .order-info { border-radius: 24px; border-radius: 60px; background: #FFFFFF; padding: 32px 28px 30px 28px; @@ -16,7 +18,7 @@ font-weight: 500; line-height: 48px; font-variation-settings: "opsz" auto; color: rgba(0, 0, 0, 0.9); color: @black-text-main; margin-left: 4px; } @@ -31,7 +33,7 @@ font-weight: normal; line-height: 36px; text-align: right; color: rgba(0, 0, 0, 0.4); color: @black-text-04; } .arrow-icon { @@ -46,8 +48,8 @@ .price-card { margin-top: 24px; border-radius: 16px; background: #F5F6FA; border-radius: 40px; background: @gary-theme; padding: 24px 32px; .mb-16 { @@ -60,7 +62,7 @@ font-weight: normal; line-height: 40px; /* 文字色/黑色/重要字 90% */ color: rgba(0, 0, 0, 0.9); color: @black-text-main; } @@ -72,7 +74,7 @@ text-align: right; font-variation-settings: "opsz" auto; /* 文字色/黑色/重要字 90% */ color: rgba(0, 0, 0, 0.9); color: @black-text-main; } .price-notice-label { @@ -84,7 +86,7 @@ font-variation-settings: "opsz" auto; /* 文字色/黑色/辅助文字 40% */ color: rgba(0, 0, 0, 0.4); color: @black-text-04; } @@ -96,13 +98,14 @@ text-align: right; font-variation-settings: "opsz" auto; /* 文字色/黑色/辅助文字 40% */ color: rgba(0, 0, 0, 0.4); color: @black-text-04; } } .total { margin-top: 32px; justify-content: flex-end; align-items: first baseline; .total-text { font-family: PingFang SC; @@ -110,16 +113,15 @@ font-weight: normal; line-height: 40px; font-variation-settings: "opsz" auto; color: rgba(0, 0, 0, 0.9); color: @black-text-main; } .num { font-size: 48px; line-height: 40px; font-variation-settings: "opsz" auto; color: rgba(0, 0, 0, 0.9); margin: 0 4px; color: @black-text-main; margin: 0 4px; } .unit { @@ -128,12 +130,12 @@ font-weight: normal; line-height: 40px; font-variation-settings: "opsz" auto; color: rgba(0, 0, 0, 0.9); color: @black-text-main; } } .faq { border-radius: 24px; border-radius: 60px; background: #FFFFFF; padding: 32px; margin-top: 24px; @@ -143,7 +145,7 @@ font-size: 32px; font-weight: 500; line-height: 48px; color: rgba(0, 0, 0, 0.9); color: @black-text-main; } @@ -164,7 +166,7 @@ line-height: 40px; font-variation-settings: "opsz" auto; /* 文字色/黑色/重要字 90% */ color: rgba(0, 0, 0, 0.9); color: @black-text-main; } .arrow-icon { @@ -199,13 +201,14 @@ font-size: 24px; font-weight: normal; line-height: 36px; color: rgba(0, 0, 0, 0.4); color: @black-text-04; } .arrow-icon { width: 20px; height: 13.6px; margin-left: 14px; margin-top: 2px; transition: transform 0.3s ease; &.rotated { @@ -216,10 +219,25 @@ } .complaint { display: flex; align-items: center; justify-content: center; margin: 0 auto; margin-top: 30px; width: 549px; height: 92px; margin-top: 42px; width: 654px; height: 102px; border-radius: 2000px; box-sizing: border-box; border: 4px solid @black-text-main; font-size: 32px; font-weight: 500; line-height: normal; text-align: center; color: @black-text-main; &.disabled { border: 4px solid @black-text-04; color: @black-text-04; } } } @@ -237,6 +255,10 @@ display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; &.flex-between-card:nth-last-of-type(1) { margin-bottom: 0; } } .color-red { @@ -245,4 +267,4 @@ .mt-16 { margin-top: 16px; } } src/robotaxi/pages/expense-description/index.tsx
@@ -20,6 +20,7 @@ const [faqData, setFaqData] = useState<FAQItem[]>([]); const [showEvaluatePopup, setShowEvaluatePopup] = useState(false); const [orderId, setOrderId] = useState(''); const [disabledComplaint, setDisabledComplaint] = useState(false); useEffect(() => { setOrderId(router.params?.orderId || ''); @@ -36,6 +37,7 @@ } const res = await getFareBreakdown(payload); setFareData(res); setDisabledComplaint(res && res.hasComplaint || false); } catch (error) { console.error('获取费用明细失败', error); showToast({ title: '获取费用明细失败', icon: 'none' }); @@ -98,6 +100,17 @@ const toRulePage = () => { Taro.navigateTo({ url: '/robotaxi/pages/pricing-rules/index' }); }; const handleClickComplaint = () => { if (disabledComplaint) { showToast({ title: '订单已有车费反馈记录,无需重复操作', icon: 'none' }); return; } setShowEvaluatePopup(true); } if (!fareData) return null; @@ -258,18 +271,22 @@ </View> )} {/* 去投诉 */} <Image className='complaint' onClick={() => setShowEvaluatePopup(true)} src='https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/complaint-btn.png' /> <View className={`complaint ${disabledComplaint ? 'disabled': ''} `} onClick={handleClickComplaint}> 不认可车费,去投诉 </View> {/* 评价弹窗 */} <EvaluatePopup show={showEvaluatePopup} orderId={orderId} orderRating={1} submitMode="complaint" submitMode="orderComplaint" hideFeedbackType disableRatingSwitch isFareFeedback onClose={() => setShowEvaluatePopup(false)} onConfirm={() => { setDisabledComplaint(true); setShowEvaluatePopup(false); }} />