完善服务模块缺少付款页的bug

This commit is contained in:
2026-06-02 11:35:31 +08:00
parent c3324d459a
commit 881262940c
35 changed files with 29069 additions and 557 deletions

View File

@@ -36,6 +36,9 @@
<text class="summary-meta-value">{{ detail.staffPhone }}</text>
</view>
</view>
<view v-if="detail.statusText == '派单未成功' && detail.summary != ''" class="dispatch-fail-banner">
<text class="dispatch-fail-text">{{ detail.summary }}</text>
</view>
</view>
<ServicePanel title="预约信息" subtitle="围绕联系人、地址和服务对象展示当前预约信息。">
@@ -73,9 +76,22 @@
<ServiceTimeline :items="detail.timeline"></ServiceTimeline>
</ServicePanel>
<view v-if="consumerViewState.showExceptionPanel" class="exception-panel">
<text class="exception-title">{{ consumerViewState.exceptionTitle }}</text>
<text class="exception-desc">{{ consumerViewState.exceptionDesc }}</text>
<view v-if="consumerViewState.exceptionReason != ''" class="exception-reason">
<text class="exception-reason-label">异常原因:</text>
<text class="exception-reason-value">{{ consumerViewState.exceptionReason }}</text>
</view>
<text class="exception-update-time">状态更新时间:{{ consumerViewState.statusUpdatedAt }}</text>
</view>
<view class="action-row">
<view class="secondary-btn" @click="bookAgain">再次预约</view>
<view v-if="detail.statusText == '派单未成功'" class="secondary-btn" @click="bookAgain">再次预约</view>
<view v-else class="secondary-btn" @click="bookAgain">再次预约</view>
<view v-if="detail.status == 'pending_acceptance'" class="primary-btn" @click="goFeedback">去验收反馈</view>
<view v-else-if="consumerViewState.showRescheduleBtn" class="primary-btn" @click="bookAgain">重新选择时间</view>
<view v-else-if="detail.statusText == '派单未成功'" class="primary-btn" @click="retryDispatch">重新派单</view>
<view v-else class="primary-btn" @click="contactService">联系客服</view>
</view>
</view>
@@ -83,14 +99,15 @@
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uvue'
import ServiceInfoList from '@/components/homeService/ServiceInfoList.uvue'
import ServicePanel from '@/components/homeService/ServicePanel.uvue'
import ServiceStatusTag from '@/components/homeService/ServiceStatusTag.uvue'
import ServiceTimeline from '@/components/homeService/ServiceTimeline.uvue'
import { fetchConsumerHomeServiceCaseDetail } from '@/services/homeServiceService.uts'
import { dispatchPaidHomecareOrder, getHomecareOrderDisplayStatus, HOMECARE_DISPATCH_STATUS_FAILED, showHomecareDispatchFailureModal } from '@/services/serviceOrderService.uts'
import { HomeServiceCaseType } from '@/types/home-service.uts'
import { getCurrentUser, getCurrentUserId } from '@/utils/store.uts'
import { goToLogin } from '@/utils/utils.uts'
@@ -130,6 +147,33 @@ function goFeedback() {
})
}
async function goPayment(): Promise<void> {
if (caseId.value == '') {
uni.showToast({ title: '订单信息异常', icon: 'none' })
return
}
if (detail.value == null) {
uni.showToast({ title: '订单信息加载失败,请稍后重试', icon: 'none' })
return
}
// 刷新一次订单状态
await loadData()
if (detail.value == null) {
uni.showToast({ title: '订单信息加载失败,请稍后重试', icon: 'none' })
return
}
if (!canPayServiceOrder.value) {
uni.showToast({ title: '当前订单已不可支付,请刷新查看最新状态', icon: 'none' })
return
}
uni.navigateTo({
url: '/pages/mall/consumer/payment'
+ '?orderId=' + encodeURIComponent(detail.value.id)
+ '&source=service'
+ '&bizType=service'
})
}
function bookAgain() {
if (detail.value == null) {
return
@@ -148,16 +192,172 @@ function contactService() {
uni.showToast({ title: '即将接入专属客服入口', icon: 'none' })
}
let isRetryDispatching = false
function retryDispatch() {
if (isRetryDispatching || detail.value == null) {
return
}
const currentId = detail.value.id
isRetryDispatching = true
uni.showLoading({ title: '正在重新派单', mask: true })
dispatchPaidHomecareOrder(currentId).then((result) => {
uni.hideLoading()
if (result.success) {
uni.showToast({ title: '派单成功', icon: 'success' })
loadData()
return
}
showHomecareDispatchFailureModal(currentId, result, (id: string) => {
retryDispatch()
})
}).catch((e) => {
uni.hideLoading()
console.error('[retryDispatch] 重新派单异常:', e)
uni.showModal({
title: '派单服务异常',
content: '派单服务暂时异常,请稍后重试',
showCancel: true,
cancelText: '稍后再试',
confirmText: '重新派单',
success: (res) => {
if (res.confirm) {
retryDispatch()
}
}
})
}).finally(() => {
isRetryDispatching = false
})
}
function getLatestTimelineRemark(caseDetail: HomeServiceCaseType): string {
if (caseDetail.timeline.length > 0) {
return caseDetail.timeline[0].description
}
return ''
}
function isTerminalStatus(status: string): boolean {
return status == 'accepted_by_user' || status == 'reviewed' || status == 'settled' || status == 'cancelled' || status == 'exception'
}
const consumerViewState = computed(() => {
const defaultState = {
showExceptionPanel: false,
exceptionTitle: '',
exceptionDesc: '',
exceptionReason: '',
statusUpdatedAt: '',
showRescheduleBtn: false,
showCancelBtn: false,
showRefundBtn: false
}
if (detail.value == null) {
return defaultState
}
const status = detail.value.status
const remark = getLatestTimelineRemark(detail.value)
const result = { ...defaultState }
if (detail.value.statusText == '派单未成功') {
result.showExceptionPanel = true
result.exceptionTitle = '派单未成功'
result.exceptionDesc = detail.value.summary != '' ? detail.value.summary : '当前暂无匹配的服务人员,请稍后重试或联系客服。'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'created' || status == 'assigned') {
result.exceptionTitle = '正在安排服务人员'
result.exceptionDesc = '您的预约申请已提交,平台正在为您匹配可上门的服务人员,请耐心等待。'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'accepted') {
result.exceptionTitle = '服务人员已接单'
result.exceptionDesc = '服务人员已确认接单,正在准备上门,请保持电话畅通。'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'departed') {
result.exceptionTitle = '服务人员正在前往'
result.exceptionDesc = '服务人员已出发,正在前往服务地点,请做好接待准备。'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'arrived' || status == 'in_service') {
result.exceptionTitle = '服务人员已到达'
result.exceptionDesc = '服务人员已到达服务地点,服务正在进行中。'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'rejected') {
result.showExceptionPanel = true
result.exceptionTitle = '当前预约暂未安排到服务人员'
result.exceptionDesc = '很抱歉,服务人员未接受该工单。您可以重新选择服务时间,或取消本次服务申请。'
result.exceptionReason = remark != '' ? remark : '服务人员未接单'
result.statusUpdatedAt = detail.value.serviceTime
result.showRescheduleBtn = true
} else if (status == 'exception') {
result.showExceptionPanel = true
result.exceptionTitle = '当前预约暂未安排到服务人员'
result.exceptionDesc = '很抱歉,当前所选预约时间暂未匹配到可服务人员。您可以重新选择服务时间,或取消本次服务申请。'
result.exceptionReason = remark != '' ? remark : '履约异常,请稍后重试或联系客服'
result.statusUpdatedAt = detail.value.serviceTime
result.showRescheduleBtn = true
} else if (status == 'cancelled') {
result.showExceptionPanel = true
result.exceptionTitle = '服务申请已取消'
result.exceptionDesc = '该服务申请已被取消。如有疑问,请联系客服了解详情。'
result.exceptionReason = remark != '' ? remark : '已取消'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'pending_acceptance') {
result.exceptionTitle = '服务已完成,等待验收'
result.exceptionDesc = '服务人员已提交服务记录,请您确认服务结果并进行评价。'
result.statusUpdatedAt = detail.value.serviceTime
} else if (status == 'completed' || status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') {
result.exceptionTitle = '服务已完成'
result.exceptionDesc = '本次服务已结束,感谢您的使用。'
result.statusUpdatedAt = detail.value.serviceTime
}
return result
})
const canPayServiceOrder = computed<boolean>(() => {
if (detail.value == null) {
return false
}
return detail.value.paymentStatus == 1
&& detail.value.status == 'created'
})
let detailRefreshTimerId: number = 0
function startDetailRefreshTimer(): void {
stopDetailRefreshTimer()
if (detail.value != null && !isTerminalStatus(detail.value.status)) {
detailRefreshTimerId = setInterval(() => {
loadData()
}, 15000)
}
}
function stopDetailRefreshTimer(): void {
if (detailRefreshTimerId > 0) {
clearInterval(detailRefreshTimerId)
detailRefreshTimerId = 0
}
}
onLoad((options) => {
const id = options['id']
if (id != null) {
caseId.value = id as string
loadData()
loadData().then(() => {
startDetailRefreshTimer()
})
}
})
onShow(() => {
loadData()
loadData().then(() => {
startDetailRefreshTimer()
})
})
onUnload(() => {
stopDetailRefreshTimer()
})
</script>
@@ -291,4 +491,66 @@ onShow(() => {
padding: 120rpx 0;
align-items: center;
}
.exception-panel {
background: #ffffff;
border-radius: 32rpx;
padding: 28rpx;
box-shadow: 0 12rpx 24rpx rgba(15, 23, 42, 0.06);
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
}
.exception-title {
font-size: 30rpx;
font-weight: 700;
color: #b45309;
margin-bottom: 12rpx;
}
.exception-desc {
font-size: 24rpx;
color: #66788a;
line-height: 36rpx;
margin-bottom: 16rpx;
}
.exception-reason {
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 12rpx;
}
.exception-reason-label {
font-size: 24rpx;
color: #66788a;
font-weight: 600;
}
.exception-reason-value {
font-size: 24rpx;
color: #b45309;
}
.exception-update-time {
font-size: 22rpx;
color: #94a3b8;
}
.dispatch-fail-banner {
margin-top: 18rpx;
padding: 18rpx 24rpx;
background: #fff7ed;
border-radius: 16rpx;
border-width: 1rpx;
border-style: solid;
border-color: #fed7aa;
}
.dispatch-fail-text {
font-size: 26rpx;
color: #c2410c;
line-height: 40rpx;
}
</style>