忽略本地报错信息文件
This commit is contained in:
@@ -47,9 +47,22 @@
|
||||
|
||||
<view class="jd-header-placeholder" :style="{ height: (activeTopModule == 'home' ? headerPlaceholderHeight : navbarTotalHeight) + 'px' }"></view>
|
||||
|
||||
<scroll-view
|
||||
direction="vertical"
|
||||
<!-- #ifdef WEB || H5 -->
|
||||
<scroll-view
|
||||
direction="vertical"
|
||||
class="main-scroll main-scroll-web"
|
||||
:style="{ height: mainScrollHeight + 'px' }"
|
||||
:lower-threshold="50"
|
||||
@scrolltolower="handleMainScrollToLower"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 -->
|
||||
<!-- #ifndef WEB -->
|
||||
<scroll-view
|
||||
direction="vertical"
|
||||
class="main-scroll"
|
||||
:style="{ height: mainScrollHeight + 'px' }"
|
||||
refresher-enabled
|
||||
:refresher-triggered="refreshing"
|
||||
:lower-threshold="50"
|
||||
@@ -57,6 +70,8 @@
|
||||
@scrolltolower="handleMainScrollToLower"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<!-- #endif -->
|
||||
<!-- #endif -->
|
||||
<HomeMallContent
|
||||
v-if="activeTopModule == 'home'"
|
||||
:current-category="currentCategory"
|
||||
@@ -191,7 +206,14 @@
|
||||
</view>
|
||||
|
||||
<view class="safe-area" :style="{ height: bottomSafeArea + 88 + 'px' }"></view>
|
||||
<!-- #ifdef WEB || H5 -->
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 -->
|
||||
<!-- #ifndef WEB -->
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
<!-- #endif -->
|
||||
|
||||
<view
|
||||
v-if="activeTopModule == 'home' && showCategoryPanel"
|
||||
@@ -261,6 +283,7 @@ import { logSupaConfig } from '@/ak/config.uts'
|
||||
const statusBarHeight = ref(0)
|
||||
const bottomSafeArea = ref(20)
|
||||
const scrollHeight = ref(0)
|
||||
const screenWindowHeight = ref(667)
|
||||
const refreshing = ref(false)
|
||||
const loading = ref(false)
|
||||
const isFirstShow = ref(true)
|
||||
@@ -298,6 +321,13 @@ const searchRowStyle = ref('')
|
||||
const activeTopModule = ref('home')
|
||||
const searchKeyword = ref('')
|
||||
|
||||
const MIN_MAIN_SCROLL_HEIGHT = 320
|
||||
const mainScrollHeight = computed((): number => {
|
||||
const headerHeight = activeTopModule.value == 'home' ? headerPlaceholderHeight.value : navbarTotalHeight.value
|
||||
const availableHeight = screenWindowHeight.value - headerHeight
|
||||
return availableHeight > MIN_MAIN_SCROLL_HEIGHT ? availableHeight : MIN_MAIN_SCROLL_HEIGHT
|
||||
})
|
||||
|
||||
type HeaderModuleItem = {
|
||||
key: string
|
||||
label: string
|
||||
@@ -2102,10 +2132,15 @@ const initPage = () => {
|
||||
statusBarHeight.value = windowInfo.statusBarHeight != null ? windowInfo.statusBarHeight : 20
|
||||
screenWidth = windowInfo.screenWidth != null ? windowInfo.screenWidth : (windowInfo.windowWidth != null ? windowInfo.windowWidth : 375)
|
||||
screenHeight = windowInfo.screenHeight != null ? windowInfo.screenHeight : (windowInfo.windowHeight != null ? windowInfo.windowHeight : 667)
|
||||
screenWindowHeight.value = screenHeight
|
||||
safeAreaBottom = windowInfo.safeArea != null ? windowInfo.safeArea.bottom : 0
|
||||
} catch (e) {
|
||||
statusBarHeight.value = 20
|
||||
screenWindowHeight.value = screenHeight
|
||||
}
|
||||
// #ifdef WEB || H5
|
||||
statusBarHeight.value = 0
|
||||
// #endif
|
||||
const searchContentHeight = Math.round(68 * screenWidth / 750)
|
||||
const searchTopGap = Math.round(14 * screenWidth / 750)
|
||||
const headerBottomPadding = Math.round(10 * screenWidth / 750)
|
||||
@@ -2144,6 +2179,9 @@ const initPage = () => {
|
||||
headerPlaceholderHeight.value = navbarTotalHeight.value + categoryBarHeightPx.value
|
||||
const safeBottom = safeAreaBottom > 0 ? (screenHeight - safeAreaBottom) : 20
|
||||
bottomSafeArea.value = safeBottom > 0 ? safeBottom : 20
|
||||
// #ifdef WEB || H5
|
||||
bottomSafeArea.value = 0
|
||||
// #endif
|
||||
|
||||
isMobile.value = screenWidth < 768
|
||||
}
|
||||
@@ -2604,6 +2642,39 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* #ifdef WEB || H5 */
|
||||
.medic-home {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.home-main-stage {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.jd-header-placeholder {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.main-scroll-web {
|
||||
flex: none;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.home-skeleton-overlay {
|
||||
position: fixed;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.service-home-section {
|
||||
background-color: #f4f8fb;
|
||||
padding: 16rpx 16rpx 32rpx;
|
||||
@@ -3173,6 +3244,18 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* #ifdef WEB || H5 */
|
||||
.main-scroll-web {
|
||||
flex: none;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.service-home-section {
|
||||
min-height: auto;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* 智能健康卡片 */
|
||||
.smart-health-card {
|
||||
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
|
||||
|
||||
@@ -102,33 +102,22 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格明细 -->
|
||||
<view class="section-card price-section">
|
||||
<view class="price-grid">
|
||||
<view class="price-item-inline">
|
||||
<text class="price-item-label">商品</text>
|
||||
<text class="price-item-value">¥{{ totalAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="price-item-inline">
|
||||
<text class="price-item-label">运费</text>
|
||||
<text class="price-item-value">+¥{{ deliveryFee.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view v-if="discountAmount > 0" class="price-item-inline">
|
||||
<text class="price-item-label">优惠</text>
|
||||
<text class="price-item-value discount-text">-¥{{ discountAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="safe-area-bottom"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部结算栏 -->
|
||||
<view class="footer-action-bar">
|
||||
<view class="footer-left">
|
||||
<text class="footer-total-label">合计:</text>
|
||||
<text class="footer-currency">¥</text>
|
||||
<text class="footer-price">{{ actualAmount.toFixed(2) }}</text>
|
||||
<view class="footer-pay-row">
|
||||
<text class="footer-pay-label">实付款</text>
|
||||
<text class="footer-currency">¥</text>
|
||||
<text class="footer-price">{{ actualAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="footer-sub-row">
|
||||
<text v-if="deliveryFee == 0" class="footer-shipping-text">免运费</text>
|
||||
<text v-else class="footer-shipping-text">运费 ¥{{ deliveryFee.toFixed(2) }}</text>
|
||||
<text v-if="totalDiscountAmount > 0" class="footer-discount-text">共优惠 ¥{{ totalDiscountAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="footer-submit-btn" @click="submitOrder">提交订单</button>
|
||||
</view>
|
||||
@@ -537,6 +526,30 @@ const discountAmount = computed(() => {
|
||||
return coupon.discount_value
|
||||
})
|
||||
|
||||
const originalTotalAmount = computed(() => {
|
||||
if (checkoutItems.value.length == 0) return 0
|
||||
return checkoutItems.value.reduce((sum, item) => {
|
||||
if (item == null) return sum
|
||||
const price = item.price
|
||||
const quantity = item.quantity
|
||||
if (Number.isNaN(price) || Number.isNaN(quantity) || price <= 0 || quantity <= 0) {
|
||||
return sum
|
||||
}
|
||||
return sum + (price * quantity)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
const memberDiscountAmount = computed(() => {
|
||||
const original = originalTotalAmount.value
|
||||
const actual = totalAmount.value
|
||||
const diff = original - actual
|
||||
return diff > 0 ? diff : 0
|
||||
})
|
||||
|
||||
const totalDiscountAmount = computed(() => {
|
||||
return memberDiscountAmount.value + discountAmount.value
|
||||
})
|
||||
|
||||
const actualAmount = computed(() => {
|
||||
// 确保所有值都是数字类型
|
||||
const total = typeof totalAmount.value === 'number' ? totalAmount.value : 0
|
||||
@@ -1903,13 +1916,15 @@ const goToLogin = () => {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
min-height: 70px;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
padding-top: 8px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
border-top: 1px solid #f0f0f0;
|
||||
@@ -1918,8 +1933,9 @@ const goToLogin = () => {
|
||||
|
||||
.footer-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-total-label {
|
||||
@@ -1940,6 +1956,36 @@ const goToLogin = () => {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footer-pay-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.footer-pay-label {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.footer-sub-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.footer-shipping-text {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.footer-discount-text {
|
||||
font-size: 11px;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.footer-submit-btn {
|
||||
background-color: #ff5000;
|
||||
color: #ffffff;
|
||||
@@ -1954,7 +2000,7 @@ const goToLogin = () => {
|
||||
}
|
||||
|
||||
.safe-area-bottom {
|
||||
height: 100px; /* 留出底部操作栏和安全区的空间 */
|
||||
height: 140px; /* 留出底部操作栏和安全区的空间 */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<text class="summary-title">{{ detail.serviceName }}</text>
|
||||
<text class="summary-case-no">服务单号:{{ detail.caseNo }}</text>
|
||||
</view>
|
||||
<ServiceStatusTag :text="detail.statusText" :tone="detail.statusTone"></ServiceStatusTag>
|
||||
<ServiceStatusTag :text="displayStatusText" :tone="displayStatusTone"></ServiceStatusTag>
|
||||
</view>
|
||||
<text class="summary-desc">{{ detail.summary }}</text>
|
||||
<view class="summary-price-row">
|
||||
@@ -102,13 +102,29 @@
|
||||
<view class="primary-btn" @click="bookAgain">再次预约</view>
|
||||
</view>
|
||||
<view v-else class="action-row">
|
||||
<view v-if="detail.statusText == '派单未成功'" class="secondary-btn" @click="bookAgain">再次预约</view>
|
||||
<view v-if="isArrivalPending" class="secondary-btn" @click="confirmArrival(false, '用户反馈服务人员未实际到达')">未到达 / 有异议</view>
|
||||
<view v-else-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="isArrivalPending" class="primary-btn" @click="confirmArrival(true, '用户确认服务人员已到达')">确认已到达</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>
|
||||
|
||||
<!-- ARRIVAL_PENDING: 确认到达模块 -->
|
||||
<view v-if="isArrivalPending" class="arrival-confirm-card">
|
||||
<view class="arrival-confirm-header">
|
||||
<text class="arrival-confirm-title">等待消费者确认到达</text>
|
||||
</view>
|
||||
<view class="arrival-confirm-desc">
|
||||
<text>服务人员已提交到达签到,请确认其是否已到达服务地点。</text>
|
||||
</view>
|
||||
<view class="arrival-confirm-actions">
|
||||
<view class="arrival-confirm-secondary-btn" @click="confirmArrival(false, '用户反馈服务人员未实际到达')">未到达 / 有异议</view>
|
||||
<view class="arrival-confirm-primary-btn" @click="confirmArrival(true, '用户确认服务人员已到达')">确认已到达</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</ServicePageScaffold>
|
||||
</template>
|
||||
@@ -124,17 +140,81 @@ 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, HOMECARE_DISPATCH_STATUS_FAILED, showHomecareDispatchFailureModal } from '@/services/serviceOrderService.uts'
|
||||
import { dispatchPaidHomecareOrder, HOMECARE_DISPATCH_STATUS_FAILED, showHomecareDispatchFailureModal, confirmHomecareArrival } from '@/services/serviceOrderService.uts'
|
||||
import { HomeServiceCaseType } from '@/types/home-service.uts'
|
||||
import { getCurrentUser, getCurrentUserId } from '@/utils/store.uts'
|
||||
import { goToLogin } from '@/utils/utils.uts'
|
||||
|
||||
const ENABLE_ARRIVAL_CONFIRM = false
|
||||
|
||||
const caseId = ref('')
|
||||
const detail = ref<HomeServiceCaseType | null>(null)
|
||||
const consumerOrderTraceId = ref('')
|
||||
|
||||
function normalizeOrderStatusCode(status: string): string {
|
||||
if (status == null) return ''
|
||||
return status.trim().toUpperCase()
|
||||
}
|
||||
|
||||
function isArrivalPendingStatus(status: string): boolean {
|
||||
const s = normalizeOrderStatusCode(status)
|
||||
return s == 'ARRIVAL_PENDING'
|
||||
|| s == 'WAITING_CONSUMER_CONFIRM'
|
||||
|| s == 'WAITING_ARRIVAL_CONFIRM'
|
||||
}
|
||||
|
||||
function consumerLog(step: string, message: string, data: any | null = null) {
|
||||
const tid = consumerOrderTraceId.value
|
||||
if (data == null) {
|
||||
console.log(`[CONSUMER_ORDER_DETAIL_TRACE][${step}][traceId=${tid}][${new Date().toISOString()}] ${message}`)
|
||||
} else {
|
||||
console.log(`[CONSUMER_ORDER_DETAIL_TRACE][${step}][traceId=${tid}][${new Date().toISOString()}] ${message}`, data)
|
||||
}
|
||||
}
|
||||
|
||||
function consumerWarn(step: string, message: string, data: any | null = null) {
|
||||
const tid = consumerOrderTraceId.value
|
||||
if (data == null) {
|
||||
console.warn(`[CONSUMER_ORDER_DETAIL_TRACE][${step}][traceId=${tid}][${new Date().toISOString()}] ${message}`)
|
||||
} else {
|
||||
console.warn(`[CONSUMER_ORDER_DETAIL_TRACE][${step}][traceId=${tid}][${new Date().toISOString()}] ${message}`, data)
|
||||
}
|
||||
}
|
||||
|
||||
function consumerError(step: string, message: string, e: any, extra: any | null = null) {
|
||||
const tid = consumerOrderTraceId.value
|
||||
console.error(`[CONSUMER_ORDER_DETAIL_TRACE][${step}][traceId=${tid}][${new Date().toISOString()}] ${message}`, {
|
||||
errorMessage: e != null ? String(e) : '',
|
||||
errorStack: '',
|
||||
extra: extra
|
||||
})
|
||||
}
|
||||
|
||||
const isArrivalPending = computed<boolean>(() => {
|
||||
if (!ENABLE_ARRIVAL_CONFIRM) return false
|
||||
if (detail.value == null) return false
|
||||
return isArrivalPendingStatus(detail.value.status)
|
||||
|| detail.value.waitingConsumerConfirm == true
|
||||
})
|
||||
|
||||
const displayStatusText = computed<string>(() => {
|
||||
if (detail.value == null) return ''
|
||||
if (isArrivalPending.value) return '等待消费者确认到达'
|
||||
return detail.value.statusText
|
||||
})
|
||||
|
||||
const displayStatusTone = computed<string>(() => {
|
||||
if (detail.value == null) return 'neutral'
|
||||
if (isArrivalPending.value) return 'warning'
|
||||
return detail.value.statusTone
|
||||
})
|
||||
|
||||
async function ensureLogin(): Promise<boolean> {
|
||||
const user = await getCurrentUser()
|
||||
if (user == null || getCurrentUserId() == '') {
|
||||
consumerWarn('C04_AUTH', 'redirect to login because current user is empty', {
|
||||
caseId: caseId.value
|
||||
})
|
||||
goToLogin('/pages/mall/consumer/home-service/order-detail?id=' + caseId.value)
|
||||
return false
|
||||
}
|
||||
@@ -142,14 +222,66 @@ async function ensureLogin(): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
consumerLog('C03_LOAD_DETAIL', 'loadData start', {
|
||||
caseId: caseId.value
|
||||
})
|
||||
if (caseId.value == '') {
|
||||
consumerWarn('C03_LOAD_DETAIL', 'blocked: caseId empty')
|
||||
return
|
||||
}
|
||||
if (!(await ensureLogin())) {
|
||||
const loginOk = await ensureLogin()
|
||||
consumerLog('C04_AUTH', 'ensureLogin result', {
|
||||
ok: loginOk,
|
||||
userId: getCurrentUserId()
|
||||
})
|
||||
if (!loginOk) {
|
||||
consumerWarn('C04_AUTH', 'blocked: not logged in', {
|
||||
caseId: caseId.value
|
||||
})
|
||||
detail.value = null
|
||||
return
|
||||
}
|
||||
detail.value = await fetchConsumerHomeServiceCaseDetail(caseId.value)
|
||||
try {
|
||||
consumerLog('C05_DETAIL_RPC', 'fetchConsumerHomeServiceCaseDetail request', {
|
||||
caseId: caseId.value
|
||||
})
|
||||
const result = await fetchConsumerHomeServiceCaseDetail(caseId.value)
|
||||
consumerLog('C06_DETAIL_RESULT_RAW', 'fetchConsumerHomeServiceCaseDetail raw result', result)
|
||||
detail.value = result
|
||||
consumerLog('C07_DETAIL_RESULT_PARSED', 'detail parsed', {
|
||||
id: detail.value != null ? detail.value.id : '',
|
||||
orderNo: detail.value != null ? detail.value.caseNo : '',
|
||||
status: detail.value != null ? detail.value.status : '',
|
||||
statusText: detail.value != null ? detail.value.statusText : '',
|
||||
dispatchStatus: '',
|
||||
paymentStatus: detail.value != null && detail.value.paymentStatus != null ? detail.value.paymentStatus : 0,
|
||||
checkinTime: detail.value != null ? detail.value.checkinTime : '',
|
||||
arriveTime: detail.value != null ? detail.value.checkinTime : '',
|
||||
evidenceCount: detail.value != null ? detail.value.evidenceCount : 0,
|
||||
isArrivalPending: isArrivalPending.value,
|
||||
timelineCount: detail.value != null ? detail.value.timeline.length : 0
|
||||
})
|
||||
if (detail.value != null) {
|
||||
consumerLog('C08_STATUS_CHECK', 'detail status resolved after reload', {
|
||||
status: detail.value.status,
|
||||
statusText: detail.value.statusText,
|
||||
checkinTime: detail.value.checkinTime,
|
||||
arriveTime: detail.value.checkinTime,
|
||||
timeline: detail.value.timeline
|
||||
})
|
||||
}
|
||||
if (detail.value != null && ENABLE_ARRIVAL_CONFIRM && isArrivalPending.value == true) {
|
||||
consumerLog('C08_STATUS_CHECK', 'arrival pending detected, confirm card should display', {
|
||||
status: detail.value.status,
|
||||
statusText: detail.value.statusText
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
consumerError('C05_DETAIL_RPC', 'fetchConsumerHomeServiceCaseDetail failed', e, {
|
||||
caseId: caseId.value
|
||||
})
|
||||
detail.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function goFeedback() {
|
||||
@@ -213,6 +345,126 @@ function contactService() {
|
||||
uni.showToast({ title: '即将接入专属客服入口', icon: 'none' })
|
||||
}
|
||||
|
||||
async function confirmArrival(confirm: boolean, reason: string): Promise<void> {
|
||||
consumerLog('C09_CONFIRM_ARRIVAL', 'confirmArrival start', {
|
||||
id: detail.value != null ? detail.value.id : '',
|
||||
status: detail.value != null ? detail.value.status : '',
|
||||
confirm: confirm,
|
||||
reason: reason
|
||||
})
|
||||
if (detail.value == null || detail.value.id == '') {
|
||||
consumerWarn('C09_CONFIRM_ARRIVAL', 'blocked: detail missing', {
|
||||
confirm: confirm,
|
||||
reason: reason
|
||||
})
|
||||
uni.showToast({ title: '订单信息异常', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({ title: confirm ? '正在确认' : '正在提交反馈', mask: true })
|
||||
try {
|
||||
consumerLog('C10_CONFIRM_RPC', 'confirmHomecareArrival request', {
|
||||
id: detail.value.id,
|
||||
confirm: confirm,
|
||||
reason: reason
|
||||
})
|
||||
const result = await confirmHomecareArrival(detail.value.id, confirm, reason)
|
||||
consumerLog('C11_CONFIRM_RESULT', 'confirmHomecareArrival result', result)
|
||||
uni.hideLoading()
|
||||
|
||||
if (confirm) {
|
||||
uni.showToast({ title: '已确认到达', icon: 'success' })
|
||||
} else {
|
||||
uni.showToast({ title: '已提交反馈', icon: 'none' })
|
||||
}
|
||||
|
||||
// 重新拉取订单详情
|
||||
await loadData()
|
||||
consumerLog('C12_CONFIRM_RELOAD', 'reload after confirmArrival finished', {
|
||||
status: detail.value != null ? detail.value.status : '',
|
||||
statusText: detail.value != null ? detail.value.statusText : ''
|
||||
})
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
consumerError('C10_CONFIRM_RPC', 'confirmHomecareArrival failed', e, {
|
||||
id: detail.value != null ? detail.value.id : '',
|
||||
confirm: confirm,
|
||||
reason: reason
|
||||
})
|
||||
uni.showToast({ title: '操作失败,请稍后重试', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
function goHome() {
|
||||
uni.navigateTo({ url: '/pages/mall/consumer/home-service/index' })
|
||||
}
|
||||
|
||||
function getPayExpireMs(caseDetail: HomeServiceCaseType): number {
|
||||
if (caseDetail.payExpireAt == null || caseDetail.payExpireAt == '') {
|
||||
return 0
|
||||
}
|
||||
const parsed = Date.parse(caseDetail.payExpireAt)
|
||||
return isNaN(parsed) ? 0 : parsed
|
||||
}
|
||||
|
||||
function isPaymentTimeExpired(caseDetail: HomeServiceCaseType): boolean {
|
||||
if (caseDetail.paymentStatus != 1 || caseDetail.status != 'created') {
|
||||
return false
|
||||
}
|
||||
const expireMs = getPayExpireMs(caseDetail)
|
||||
if (expireMs <= 0) {
|
||||
return false
|
||||
}
|
||||
return expireMs <= Date.now()
|
||||
}
|
||||
|
||||
const isServicePaymentExpired = computed<boolean>(() => {
|
||||
if (detail.value == null) {
|
||||
return false
|
||||
}
|
||||
return isPaymentTimeExpired(detail.value)
|
||||
})
|
||||
|
||||
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()
|
||||
console.log('[retryDispatch] 派单结果:', JSON.stringify(result))
|
||||
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
|
||||
@@ -258,6 +510,11 @@ const consumerViewState = computed(() => {
|
||||
result.exceptionTitle = '服务人员已到达'
|
||||
result.exceptionDesc = '服务人员已到达服务地点,服务正在进行中。'
|
||||
result.statusUpdatedAt = detail.value.serviceTime
|
||||
} else if (isArrivalPending.value) {
|
||||
result.showExceptionPanel = true
|
||||
result.exceptionTitle = '待确认到达'
|
||||
result.exceptionDesc = '服务人员已到达现场并提交签到,请确认是否已到达。'
|
||||
result.statusUpdatedAt = detail.value.serviceTime
|
||||
} else if (status == 'rejected') {
|
||||
result.showExceptionPanel = true
|
||||
result.exceptionTitle = '当前预约暂未安排到服务人员'
|
||||
@@ -310,16 +567,31 @@ function stopDetailRefreshTimer(): void {
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
consumerOrderTraceId.value = Date.now().toString()
|
||||
consumerLog('C01_ROUTE', 'consumer order detail onLoad start', {
|
||||
rawOptions: options,
|
||||
caseId: options != null && options['id'] != null ? String(options['id']) : ''
|
||||
})
|
||||
const id = options['id']
|
||||
if (id != null) {
|
||||
caseId.value = id as string
|
||||
consumerLog('C01_ROUTE', 'caseId resolved', {
|
||||
caseId: caseId.value
|
||||
})
|
||||
loadData().then(() => {
|
||||
startDetailRefreshTimer()
|
||||
})
|
||||
} else {
|
||||
consumerWarn('C01_ROUTE', 'missing caseId, stop load', {
|
||||
rawOptions: options
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
consumerLog('C02_PAGE_SHOW', 'page show, reload detail', {
|
||||
caseId: caseId.value
|
||||
})
|
||||
loadData().then(() => {
|
||||
startDetailRefreshTimer()
|
||||
})
|
||||
@@ -506,4 +778,72 @@ onUnload(() => {
|
||||
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;
|
||||
}
|
||||
|
||||
.arrival-confirm-card {
|
||||
background: linear-gradient(180deg, #fffbeb 0%, #fef3c7 100%);
|
||||
border-radius: 32rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 12rpx 24rpx rgba(180, 83, 9, 0.1);
|
||||
margin-bottom: 24rpx;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.arrival-confirm-header {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.arrival-confirm-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.arrival-confirm-desc {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.arrival-confirm-actions {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.arrival-confirm-secondary-btn,
|
||||
.arrival-confirm-primary-btn {
|
||||
flex: 1;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 24rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.arrival-confirm-secondary-btn {
|
||||
background: #ffffff;
|
||||
color: #66788a;
|
||||
border-width: 2rpx;
|
||||
border-style: solid;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.arrival-confirm-primary-btn {
|
||||
background: #f59e0b;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1667,7 +1667,9 @@ const redispatchServiceOrder = async (order: OrderItem) => {
|
||||
isRedispatching = true
|
||||
uni.showLoading({ title: '正在重新派单', mask: true })
|
||||
try {
|
||||
console.log('[redispatchServiceOrder] 开始重新派单,orderId:', order.id)
|
||||
const result = await dispatchPaidHomecareOrder(order.id)
|
||||
console.log('[redispatchServiceOrder] 派单结果:', JSON.stringify(result))
|
||||
uni.hideLoading()
|
||||
if (result.success) {
|
||||
uni.showToast({ title: '派单成功', icon: 'success' })
|
||||
|
||||
@@ -1475,7 +1475,9 @@ const confirmPayment = async () => {
|
||||
isDispatchSubmitting.value = true
|
||||
uni.showLoading({ title: '正在安排服务', mask: true })
|
||||
try {
|
||||
console.log('[confirmPayment] 开始派单,orderId:', orderId.value)
|
||||
const dispatchResult = await dispatchPaidHomecareOrder(orderId.value)
|
||||
console.log('[confirmPayment] 派单结果:', JSON.stringify(dispatchResult))
|
||||
uni.hideLoading()
|
||||
if (dispatchResult.success) {
|
||||
uni.redirectTo({
|
||||
|
||||
Reference in New Issue
Block a user