将组件级 i18n 对象重构为统一由全局 locales/messages.js 管理,确保中英文语言切换时正确显示对应文本。 - 新增 utils/i18n.js 国际化工具函数 - 更新 store/$t.mixin.js 翻译混入逻辑 - 迁移 pkgService、pkgBooking、pkgAccount、pkgExplore、pages 等共 58 个组件的中英文文本至全局消息表
233 lines
6.5 KiB
Vue
233 lines
6.5 KiB
Vue
<template>
|
|
<view class="booking-list-page tn-safe-area-inset-bottom">
|
|
<tn-nav-bar :isBack="true" :bottomShadow="false" backgroundColor="transparent">
|
|
<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="empty-title">{{ $tr('订单加载中') }}</view>
|
|
<view class="empty-desc tn-margin-top-sm">{{ $tr('正在同步您的真实预订记录,请稍候。') }}</view>
|
|
</view>
|
|
<view v-else-if="bookingList.length">
|
|
<view
|
|
v-for="item in bookingList"
|
|
:key="item.orderNo"
|
|
class="booking-card tn-margin-bottom-lg"
|
|
@click="goDetail(item)"
|
|
>
|
|
<view class="tn-flex tn-flex-row-between tn-flex-col-center">
|
|
<view>
|
|
<view class="booking-title">{{ item.roomTypeText || $tr('房源预订') }}</view>
|
|
<view class="booking-subtitle tn-margin-top-xs">{{ item.orderNo }}</view>
|
|
</view>
|
|
<view class="status-badge" :class="'status-' + item.tagType">
|
|
{{ item.statusText }}
|
|
</view>
|
|
</view>
|
|
|
|
<view class="info-grid 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">{{ item.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">{{ item.startDateText }}{{ $tr(' 至 ') }}{{ item.endDateText }}</text>
|
|
</view>
|
|
<view class="info-row tn-flex tn-flex-row-between">
|
|
<text class="info-label">{{ $tr('提交时间') }}</text>
|
|
<text class="info-value">{{ item.createdAtText }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else class="empty-card tn-padding-lg">
|
|
<view class="empty-title">{{ $tr('暂无预订记录') }}</view>
|
|
<view class="empty-desc tn-margin-top-sm">{{ $tr('当前暂无预订记录,完成预订后会在这里展示。') }}</view>
|
|
<view class="custom-btn btn-primary tn-margin-top-lg" @click="goSelectRoom">
|
|
<text class="tn-text-bold">{{ $tr('去选房') }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { orderSearchedMePage } from '@/api/index.js'
|
|
import { getSessionSnapshot } from '../../utils/app-state'
|
|
import { normalizeBookingList, parseOrderListResponse } from '../../utils/booking-helper'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
bookingList: [],
|
|
loading: false
|
|
}
|
|
},
|
|
computed: {
|
|
language() {
|
|
return this.$getLanguage()
|
|
}
|
|
},
|
|
watch: {
|
|
language() {
|
|
this.fetchBookingList()
|
|
}
|
|
},
|
|
async onShow() {
|
|
await this.fetchBookingList()
|
|
},
|
|
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
|
|
},
|
|
applyBookingList(list = []) {
|
|
this.bookingList = Array.isArray(list) ? list : []
|
|
},
|
|
fetchBookingList() {
|
|
if (!this.isCurrentLogin()) {
|
|
this.applyBookingList()
|
|
this.loading = false
|
|
return Promise.resolve()
|
|
}
|
|
|
|
this.loading = true
|
|
return orderSearchedMePage({
|
|
PageIndex: 1,
|
|
PageSize: 100,
|
|
IsExport: false
|
|
})
|
|
.then((response) => {
|
|
this.applyBookingList(normalizeBookingList(parseOrderListResponse(response), this.language))
|
|
})
|
|
.catch((error) => {
|
|
console.error('fetch booking list failed', error)
|
|
const storeBookingList = this.getStoreBookingList()
|
|
const session = getSessionSnapshot()
|
|
const cachedList = storeBookingList.length ? storeBookingList : session.bookingList
|
|
this.applyBookingList(normalizeBookingList(cachedList, this.language))
|
|
uni.showToast({
|
|
title: this.bookingList.length
|
|
? this.$tr('网络异常,已展示最近同步记录')
|
|
: ((error && (error.message || error.msg)) || this.$tr('预订记录加载失败')),
|
|
icon: 'none'
|
|
})
|
|
})
|
|
.finally(() => {
|
|
this.loading = false
|
|
})
|
|
},
|
|
goDetail(item) {
|
|
uni.navigateTo({
|
|
url: `/pkgBooking/booking_detail/booking_detail?orderNo=${item.orderNo}`
|
|
})
|
|
},
|
|
goSelectRoom() {
|
|
uni.reLaunch({
|
|
url: '/pages/index/index?index=0&pageId=select-room'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.booking-list-page {
|
|
min-height: 100vh;
|
|
background: linear-gradient(180deg, #F0F6FF 0%, #F8FAFF 100%);
|
|
}
|
|
|
|
.booking-card,
|
|
.empty-card {
|
|
background: #ffffff;
|
|
border-radius: 24rpx;
|
|
padding: 30rpx;
|
|
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.04);
|
|
border: 1rpx solid rgba($brand-primary, 0.05);
|
|
}
|
|
|
|
.booking-title,
|
|
.empty-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #1A2233;
|
|
}
|
|
|
|
.booking-subtitle,
|
|
.empty-desc,
|
|
.info-label {
|
|
color: #6A7A99;
|
|
}
|
|
|
|
.info-value {
|
|
max-width: 58%;
|
|
text-align: right;
|
|
color: #1A2233;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.info-row {
|
|
padding: 16rpx 0;
|
|
border-bottom: 1px dashed rgba($brand-primary, 0.15);
|
|
}
|
|
|
|
.info-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 8rpx 20rpx;
|
|
border-radius: 999rpx;
|
|
font-size: 22rpx;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.status-pending {
|
|
background: rgba(245, 158, 11, 0.12);
|
|
color: #B45309;
|
|
}
|
|
|
|
.status-approved {
|
|
background: rgba($brand-primary, 0.12);
|
|
color: $brand-primary;
|
|
}
|
|
|
|
.status-rejected {
|
|
background: rgba(239, 68, 68, 0.12);
|
|
color: #B91C1C;
|
|
}
|
|
|
|
.custom-btn {
|
|
width: 100%;
|
|
height: 96rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 999rpx;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.custom-btn:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, rgba($brand-primary, 0.9) 0%, $brand-primary 100%);
|
|
color: #ffffff;
|
|
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.25);
|
|
}
|
|
</style>
|