将组件级 i18n 对象重构为统一由全局 locales/messages.js 管理,确保中英文语言切换时正确显示对应文本。 - 新增 utils/i18n.js 国际化工具函数 - 更新 store/$t.mixin.js 翻译混入逻辑 - 迁移 pkgService、pkgBooking、pkgAccount、pkgExplore、pages 等共 58 个组件的中英文文本至全局消息表
276 lines
8.4 KiB
Vue
276 lines
8.4 KiB
Vue
<template>
|
|
<view class="booking-detail-page tn-safe-area-inset-bottom">
|
|
<tn-nav-bar :isBack="true" :bottomShadow="false" backgroundColor="#FFFFFF">
|
|
<view class="tn-text-bold tn-text-xl tn-color-black">{{ $tr('预订详情') }}</view>
|
|
</tn-nav-bar>
|
|
|
|
<view class="page-content tn-padding" :style="{paddingTop: vuex_custom_bar_height + 'px'}">
|
|
<view v-if="loading" class="empty-card tn-padding-lg">
|
|
<view class="booking-title">{{ $tr('订单加载中') }}</view>
|
|
<view class="demo-desc tn-margin-top-sm">{{ $tr('正在同步该预订的真实详情,请稍候。') }}</view>
|
|
</view>
|
|
<view v-else-if="booking" class="booking-card tn-padding-lg">
|
|
<view class="tn-flex tn-flex-row-between tn-flex-col-center">
|
|
<view>
|
|
<view class="booking-title">{{ $tr('预定单号') }}</view>
|
|
<view class="booking-order tn-margin-top-xs">{{ booking.orderNo }}</view>
|
|
</view>
|
|
<view class="status-badge" :class="'status-' + booking.tagType">{{ booking.statusText }}</view>
|
|
</view>
|
|
|
|
<view class="info-box tn-margin-top">
|
|
<view class="info-row tn-flex tn-flex-row-between tn-margin-bottom-sm">
|
|
<text class="info-label">{{ $tr('风格房型') }}</text>
|
|
<text class="info-value">{{ booking.roomTypeText }}</text>
|
|
</view>
|
|
<view class="info-row tn-flex tn-flex-row-between tn-margin-bottom-sm">
|
|
<text class="info-label">{{ $tr('楼栋楼层') }}</text>
|
|
<text class="info-value">{{ booking.buildingFloorText }}</text>
|
|
</view>
|
|
<view class="info-row tn-flex tn-flex-row-between tn-margin-bottom-sm">
|
|
<text class="info-label">{{ $tr('房间号') }}</text>
|
|
<text class="info-value">{{ booking.roomNoText }}</text>
|
|
</view>
|
|
<view class="info-row tn-flex tn-flex-row-between tn-margin-bottom-sm">
|
|
<text class="info-label">{{ $tr('所属区域') }}</text>
|
|
<text class="info-value">{{ booking.areaText }}</text>
|
|
</view>
|
|
<view class="info-row tn-flex tn-flex-row-between tn-margin-bottom-sm">
|
|
<text class="info-label">{{ $tr('入住时间') }}</text>
|
|
<text class="info-value">{{ booking.startDateText }}{{ $tr(' 至 ') }}{{ booking.endDateText }}</text>
|
|
</view>
|
|
<view class="info-row tn-flex tn-flex-row-between">
|
|
<text class="info-label">{{ $tr('提交时间') }}</text>
|
|
<text class="info-value">{{ booking.createdAtText }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="booking.rejectReasonText" class="reason-box tn-margin-top">
|
|
<view class="reason-title">{{ $tr('驳回原因') }}</view>
|
|
<view class="reason-content tn-margin-top-xs">{{ booking.rejectReasonText }}</view>
|
|
</view>
|
|
|
|
<view class="flow-box tn-margin-top">
|
|
<view class="flow-title">{{ $tr('当前流程') }}</view>
|
|
<view class="flow-step tn-margin-top-sm" :class="isStepActive('pending_review') ? 'active' : ''">{{ $tr('1. 已支付,等待角色审批') }}</view>
|
|
<view class="flow-step tn-margin-top-sm" :class="isStepActive('approved_wait_assign') ? 'active' : ''">{{ $tr('2. 审批通过,等待分配管家') }}</view>
|
|
<view class="flow-step tn-margin-top-sm" :class="isStepActive('assigned_wait_room') ? 'active' : ''">{{ $tr('3. 已分配管家,等待分配房号') }}</view>
|
|
</view>
|
|
|
|
</view>
|
|
|
|
<view v-else class="empty-card tn-padding-lg">
|
|
<view class="booking-title">{{ $tr('暂无预订记录') }}</view>
|
|
<view class="demo-desc tn-margin-top-sm">{{ $tr('当前暂无可查看的预订详情,请先前往预订记录确认订单。') }}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { orderSearchedDetail, orderSearchedMePage } from '@/api/index.js'
|
|
import { getSessionSnapshot } from '../../utils/app-state'
|
|
import {
|
|
normalizeBookingDetail,
|
|
normalizeBookingList,
|
|
parseOrderDetailResponse,
|
|
parseOrderListResponse
|
|
} from '../../utils/booking-helper'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
orderNo: '',
|
|
booking: null,
|
|
loading: false
|
|
}
|
|
},
|
|
computed: {
|
|
language() {
|
|
return this.$getLanguage()
|
|
}
|
|
},
|
|
watch: {
|
|
language() {
|
|
this.refreshBooking()
|
|
}
|
|
},
|
|
onLoad(options) {
|
|
this.orderNo = options.orderNo || ''
|
|
},
|
|
async onShow() {
|
|
await this.refreshBooking()
|
|
},
|
|
methods: {
|
|
getStoreUser() {
|
|
return (this.$store && this.$store.state && this.$store.state.vuex_user) || {}
|
|
},
|
|
getStoreBookingList() {
|
|
const list = this.$store && this.$store.state && this.$store.state.vuex_valid_orders
|
|
return Array.isArray(list) ? list : []
|
|
},
|
|
isCurrentLogin() {
|
|
const storeUser = this.getStoreUser()
|
|
if (storeUser && Object.keys(storeUser).length) {
|
|
return true
|
|
}
|
|
return !!getSessionSnapshot().isLogin
|
|
},
|
|
resolveBookingFromList(orderList = []) {
|
|
const list = Array.isArray(orderList) ? orderList : []
|
|
if (this.orderNo) {
|
|
return list.find(item => String(item.orderNo) === String(this.orderNo)) || null
|
|
}
|
|
return list[0] || null
|
|
},
|
|
refreshBooking() {
|
|
if (!this.isCurrentLogin()) {
|
|
this.booking = null
|
|
this.loading = false
|
|
return Promise.resolve()
|
|
}
|
|
|
|
this.loading = true
|
|
const requestPromise = this.orderNo
|
|
? orderSearchedDetail(this.orderNo)
|
|
: orderSearchedMePage({
|
|
PageIndex: 1,
|
|
PageSize: 100,
|
|
IsExport: false
|
|
})
|
|
|
|
return requestPromise
|
|
.then((response) => {
|
|
if (this.orderNo) {
|
|
this.booking = normalizeBookingDetail(parseOrderDetailResponse(response), this.language)
|
|
return
|
|
}
|
|
const orderList = normalizeBookingList(parseOrderListResponse(response), this.language)
|
|
this.booking = this.resolveBookingFromList(orderList)
|
|
})
|
|
.catch((error) => {
|
|
console.error('fetch booking detail failed', error)
|
|
const storeBookingList = this.getStoreBookingList()
|
|
const session = getSessionSnapshot()
|
|
const cachedList = storeBookingList.length ? storeBookingList : session.bookingList
|
|
this.booking = this.resolveBookingFromList(normalizeBookingList(cachedList, this.language))
|
|
uni.showToast({
|
|
title: this.booking
|
|
? this.$tr('网络异常,已展示最近同步详情')
|
|
: ((error && (error.message || error.msg)) || this.$tr('预订详情加载失败')),
|
|
icon: 'none'
|
|
})
|
|
})
|
|
.finally(() => {
|
|
this.loading = false
|
|
})
|
|
},
|
|
isStepActive(targetStatus) {
|
|
if (!this.booking) return false
|
|
const order = ['pending_review', 'approved_wait_assign', 'assigned_wait_room']
|
|
return order.indexOf(this.booking.statusKey) >= order.indexOf(targetStatus)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.booking-detail-page {
|
|
background: #f8f9fa;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.booking-card,
|
|
.empty-card {
|
|
background: #ffffff;
|
|
border-radius: 28rpx;
|
|
box-shadow: 0 12rpx 36rpx rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.booking-title,
|
|
.flow-title {
|
|
font-size: 30rpx;
|
|
font-weight: bold;
|
|
color: #222222;
|
|
}
|
|
|
|
.booking-order {
|
|
font-size: 26rpx;
|
|
color: #666666;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 10rpx 20rpx;
|
|
border-radius: 999rpx;
|
|
font-size: 24rpx;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.status-pending,
|
|
.status-pending_review {
|
|
background: rgba(255, 165, 0, 0.12);
|
|
color: #d18a00;
|
|
}
|
|
|
|
.status-approved,
|
|
.status-approved_wait_assign,
|
|
.status-assigned_wait_room {
|
|
background: rgba($brand-primary-rgb, 0.18);
|
|
color: #4f6e17;
|
|
}
|
|
|
|
.status-rejected {
|
|
background: rgba(255, 90, 95, 0.12);
|
|
color: #d34d52;
|
|
}
|
|
|
|
.info-box,
|
|
.flow-box,
|
|
.reason-box {
|
|
padding: 24rpx;
|
|
background: #f8f9fa;
|
|
border-radius: 20rpx;
|
|
}
|
|
|
|
.info-label,
|
|
.demo-desc,
|
|
.reason-content {
|
|
color: #777777;
|
|
}
|
|
|
|
.info-value {
|
|
max-width: 60%;
|
|
text-align: right;
|
|
color: #222222;
|
|
}
|
|
|
|
.flow-step {
|
|
padding: 18rpx 22rpx;
|
|
background: #ffffff;
|
|
border-radius: 18rpx;
|
|
color: #777777;
|
|
}
|
|
|
|
.flow-step.active {
|
|
background: rgba($brand-primary-rgb, 0.16);
|
|
color: #4f6e17;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.demo-btn {
|
|
padding: 16rpx 28rpx;
|
|
border-radius: 999rpx;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.demo-btn-pass {
|
|
background: rgba($brand-primary-rgb, 0.18);
|
|
color: #4f6e17;
|
|
}
|
|
|
|
.demo-btn-reject {
|
|
background: rgba(255, 90, 95, 0.12);
|
|
color: #d34d52;
|
|
}
|
|
</style>
|