refactor: 国际化文本迁移至全局 locales/messages.js

将组件级 i18n 对象重构为统一由全局 locales/messages.js 管理,确保中英文语言切换时正确显示对应文本。

- 新增 utils/i18n.js 国际化工具函数
- 更新 store/$t.mixin.js 翻译混入逻辑
- 迁移 pkgService、pkgBooking、pkgAccount、pkgExplore、pages 等共 58 个组件的中英文文本至全局消息表
This commit is contained in:
liangjian
2026-07-24 13:43:41 +08:00
parent 445bd850f4
commit 6e721ccc7e
58 changed files with 1810 additions and 4065 deletions

View File

@@ -1,7 +1,7 @@
<template>
<view class="service-page tn-safe-area-inset-bottom">
<tn-nav-bar :isBack="false" :bottomShadow="false" :backgroundColor="navBarBackgroundColor" :customBackStyle="{ transition: 'all 0.3s ease' }">
<view class="tn-text-bold tn-text-xl tn-color-black">{{ t('title') }}</view>
<view class="tn-text-bold tn-text-xl tn-color-black">{{ $tr('我的入住') }}</view>
</tn-nav-bar>
<scroll-view class="service-scroll" scroll-y :style="{paddingTop: vuex_custom_bar_height + 'px'}" @scroll="handleScroll">
@@ -13,12 +13,12 @@
<view class="icon-wrap tn-margin-bottom">
<text class="tn-icon-home-smile tn-color-white"></text>
</view>
<view class="tn-text-bold tn-color-white" style="font-size: 40rpx;">{{ t('notCheckedInTitle') }}</view>
<view class="tn-text-bold tn-color-white" style="font-size: 40rpx;">{{ $tr('您还没有在租房间') }}</view>
<view class="tn-color-white tn-margin-top-sm tn-text-center" style="opacity: 0.8; line-height: 1.6;">
{{ t('notCheckedInDesc') }}
{{ $tr('长租公寓为海外青年提供高端社区服务\n请先前往选房大厅挑选心仪套间') }}
</view>
<view class="custom-btn btn-glass tn-margin-top-xl" @click="goSelectRoom" style="width: 80%;">
<text class="tn-text-bold">{{ t('goSelectRoom') }}</text>
<text class="tn-text-bold">{{ $tr('去选房') }}</text>
</view>
</view>
</view>
@@ -49,7 +49,7 @@
<view class="ticket-header tn-flex tn-flex-row-between tn-flex-col-center">
<view class="tn-flex tn-flex-col-center">
<view class="ticket-icon-wrap"><text class="tn-icon-order-fill"></text></view>
<text class="tn-text-bold tn-text-lg tn-color-black tn-margin-left-sm">{{ t('bookingInfo') }}</text>
<text class="tn-text-bold tn-text-lg tn-color-black tn-margin-left-sm">{{ $tr('预定信息') }}</text>
</view>
<view class="ticket-status-badge">{{ bookingStatusText }}</view>
</view>
@@ -73,7 +73,7 @@
<view class="process-track tn-margin-top-xl">
<view class="track-item active">
<view class="track-dot"><text class="tn-icon-success"></text></view>
<text class="track-text">{{ t('submitBookingStep') }}</text>
<text class="track-text">{{ $tr('提交预定') }}</text>
</view>
<view class="track-line active-line"></view>
<view class="track-item" :class="isContractSigningStep ? 'active' : 'current'">
@@ -82,7 +82,7 @@
<view v-if="!isContractSigningStep" class="dot-inner"></view>
<text v-if="isContractSigningStep" class="tn-icon-success"></text>
</view>
<text class="track-text">{{ t('reviewStep') }}</text>
<text class="track-text">{{ $tr('管家审核') }}</text>
</view>
<view class="track-line" :class="{ 'active-line': isContractSigningStep }"></view>
<view class="track-item" :class="{ active: hasSignedContract, current: isContractSigningStep && !hasSignedContract }">
@@ -91,12 +91,12 @@
<view v-if="isContractSigningStep && !hasSignedContract" class="dot-inner"></view>
<text v-if="hasSignedContract" class="tn-icon-success"></text>
</view>
<text class="track-text">{{ t('contractStep') }}</text>
<text class="track-text">{{ $tr('签订合同') }}</text>
</view>
<view class="track-line" :class="{ 'active-line': hasSignedContract }"></view>
<view class="track-item">
<view class="track-dot"></view>
<text class="track-text">{{ t('checkinStep') }}</text>
<text class="track-text">{{ $tr('办理入住') }}</text>
</view>
</view>
@@ -127,11 +127,11 @@
<view class="tn-flex tn-flex-row-between tn-flex-col-center">
<view class="irc-room-no">{{ roomDisplayText }}</view>
<view class="irc-status-badge">
<text class="tn-icon-vip-fill tn-margin-right-xs"></text>{{ t('activeStatus') }}
<text class="tn-icon-vip-fill tn-margin-right-xs"></text>{{ $tr('使用中') }}
</view>
</view>
<view class="irc-room-title clamp-text-1 tn-margin-top-xs">{{ roomTitleText }}</view>
<view class="irc-lease tn-margin-top-sm">{{ t('leaseTerm') }}:{{ leaseTermText }}</view>
<view class="irc-lease tn-margin-top-sm">{{ $tr('租期') }}:{{ leaseTermText }}</view>
</view>
<!-- 环境与客控区 -->
@@ -139,7 +139,7 @@
<view class="irc-env-row tn-flex tn-flex-row-between tn-flex-col-center">
<view class="irc-env-text">{{ smartSummaryText }}</view>
<view class="irc-smart-btn" @click="goSmartControl">
{{ t('smartControl') }} <text class="tn-icon-right tn-margin-left-xs"></text>
{{ $tr('全屋智能') }} <text class="tn-icon-right tn-margin-left-xs"></text>
</view>
</view>
@@ -158,7 +158,7 @@
</view>
<view v-else class="irc-qc-empty" @click.stop="goSmartControl">
<text class="tn-icon-add tn-margin-right-xs"></text>
{{ t('addSmartDevice') }}
{{ $tr('添加智能设备') }}
</view>
</scroll-view>
</view>
@@ -166,7 +166,7 @@
</view>
<view class="tn-margin-top tn-margin-left tn-margin-right">
<view class="tn-text-bold tn-text tn-color-black tn-margin-bottom">{{ t('stayService') }}</view>
<view class="tn-text-bold tn-text tn-color-black tn-margin-bottom">{{ $tr('住中申请') }}</view>
<view class="tn-flex tn-flex-wrap tn-flex-row-between">
<view class="service-item tn-margin-bottom-sm" v-for="item in stayActions" :key="item.key" @click="openServicePage(item)">
@@ -193,8 +193,8 @@
<view class="tn-margin-top-sm tn-margin-left tn-margin-right">
<view class="tn-flex tn-flex-row-between tn-flex-col-bottom tn-margin-bottom">
<view class="tn-text-bold tn-text tn-color-black">{{ t('publicService') }}</view>
<view class="tn-text-sm tn-color-gray">{{ t('viewAll') }} <text class="tn-icon-right section-arrow-icon"></text></view>
<view class="tn-text-bold tn-text tn-color-black">{{ $tr('公寓精选服务') }}</view>
<view class="tn-text-sm tn-color-gray">{{ $tr('全部') }} <text class="tn-icon-right section-arrow-icon"></text></view>
</view>
<view class="tn-flex tn-flex-wrap tn-flex-row-between">
@@ -222,28 +222,28 @@
<view class="bill-section tn-margin-top tn-margin-left tn-margin-right tn-padding-lg">
<view class="tn-flex tn-flex-row-between tn-flex-col-center tn-margin-bottom">
<view class="tn-text-bold tn-text tn-color-black">{{ t('myBill') }}</view>
<view class="status-tag tn-text-xs" :class="unpaidBillCount ? 'tag-busy' : 'tag-free'">{{ unpaidBillCount ? t('unpaid') : t('paidOff') }}</view>
<view class="tn-text-bold tn-text tn-color-black">{{ $tr('本月账单') }}</view>
<view class="status-tag tn-text-xs" :class="unpaidBillCount ? 'tag-busy' : 'tag-free'">{{ unpaidBillCount ? $tr('待支付') : $tr('已结清') }}</view>
</view>
<view class="tn-flex tn-flex-row-between tn-flex-col-bottom tn-margin-bottom-sm">
<view class="tn-text-bold tn-color-black" style="font-size: 60rpx;">
<text class="tn-text-lg tn-margin-right-xs">¥</text>{{ formatMoney(unpaidBillTotal) }}
</view>
<view class="custom-btn btn-primary" style="padding: 10rpx 30rpx;" @click.stop="goUtilityRecharge">
<text class="tn-text-sm tn-text-bold">{{ unpaidBillCount ? t('payBill') : t('viewBill') }}</text>
<text class="tn-text-sm tn-text-bold">{{ unpaidBillCount ? $tr('去支付') : $tr('查看账单') }}</text>
</view>
</view>
<view class="tn-flex tn-flex-row-between tn-margin-top">
<view class="tn-flex tn-flex-direction-column">
<text class="tn-color-gray tn-text-xs">{{ t('billType') }}</text>
<text class="tn-color-gray tn-text-xs">{{ $tr('账单类型') }}</text>
<text class="tn-text-bold tn-text-sm tn-margin-top-xs tn-color-black clamp-text-1">{{ unpaidBillPrimaryType }}</text>
</view>
<view class="tn-flex tn-flex-direction-column">
<text class="tn-color-gray tn-text-xs">{{ t('billCount') }}</text>
<text class="tn-text-bold tn-text-sm tn-margin-top-xs tn-color-black">{{ unpaidBillCount }} {{ t('billUnit') }}</text>
<text class="tn-color-gray tn-text-xs">{{ $tr('账单数量') }}</text>
<text class="tn-text-bold tn-text-sm tn-margin-top-xs tn-color-black">{{ unpaidBillCount }} {{ $tr('笔') }}</text>
</view>
<view class="tn-flex tn-flex-direction-column">
<text class="tn-color-gray tn-text-xs">{{ t('lastCreateTime') }}</text>
<text class="tn-color-gray tn-text-xs">{{ $tr('最近生成') }}</text>
<text class="tn-text-bold tn-text-sm tn-margin-top-xs tn-color-black">{{ latestBillTimeText }}</text>
</view>
</view>
@@ -251,7 +251,7 @@
<!-- 宿管与管理员列表 -->
<view class="butler-card-new tn-margin-top tn-margin-left tn-margin-right">
<view class="tn-text-bold tn-text tn-color-black tn-margin-bottom">{{ t('managementContacts') }}</view>
<view class="tn-text-bold tn-text tn-color-black tn-margin-bottom">{{ $tr('宿管与管理员') }}</view>
<view class="butler-card-new__inner">
<view v-if="managerContacts.length" class="butler-card-new__list">
<view
@@ -271,10 +271,10 @@
<view class="butler-member-new__name">
{{ getManagerName(contact) }}
<text class="butler-member-new__tag butler-member-new__tag--type" :class="contact.contactType === 'counselor' ? 'tag-counselor' : 'tag-dormitory'">
{{ contact.contactType === 'counselor' ? t('counselorTag') : t('dormitoryManagerTag') }}
{{ contact.contactType === 'counselor' ? $tr('管理员') : $tr('宿管') }}
</text>
</view>
<view class="butler-member-new__phone">{{ getManagerPhone(contact) || t('noContactPhone') }}</view>
<view class="butler-member-new__phone">{{ getManagerPhone(contact) || $tr('暂无联系电话') }}</view>
</view>
</view>
<view
@@ -295,8 +295,8 @@
</view>
</view>
<view class="butler-member-new__info">
<view class="butler-member-new__name">{{ t('managementContacts') }}</view>
<view class="butler-member-new__phone">{{ isManagementContactsLoading ? t('loadingButlerInfo') : t('noManagerContacts') }}</view>
<view class="butler-member-new__name">{{ $tr('宿管与管理员') }}</view>
<view class="butler-member-new__phone">{{ isManagementContactsLoading ? $tr('正在同步管家信息...') : $tr('暂无管理联系人') }}</view>
</view>
</view>
</view>
@@ -562,176 +562,6 @@ export default {
descEn: 'Submit lost or found item details so the community can help.'
}
],
i18n: {
zh: {
title: '我的入住',
bookingInfo: '预定信息',
submitBookingStep: '提交预定',
reviewStep: '管家审核',
contractStep: '签订合同',
checkinStep: '办理入住',
notCheckedInTitle: '您还没有在租房间',
notCheckedInDesc: '长租公寓为海外青年提供高端社区服务\n请先前往选房大厅挑选心仪套间',
bookingPendingTitle: '您的预订正在审核中',
bookingPendingDesc: '定金已支付,预定单正在审批流中。审批通过后将分配管家并等待分房号。',
goSelectRoom: '去选房',
stayService: '住中申请',
butler: '专属社区管家',
online: '当前在线 · 响应时间 < 5分钟',
callNow: '拨打',
publicService: '公寓精选服务',
viewAll: '全部',
leaseTerm: '租期',
activeStatus: '使用中',
myBill: '本月账单',
unpaid: '待支付',
paidOff: '已结清',
payBill: '去支付',
viewBill: '查看账单',
smartControl: '全屋智能',
addSmartDevice: '添加智能设备',
smartAvailableAfterCheckin: '入住后可使用智能设备',
syncingSmartDevices: '正在同步智能设备...',
smartDeviceConnected: '已接入 {count} 台智能设备',
smartDeviceSyncFailed: '智能设备同步失败',
smartDeviceEmpty: '当前房间暂无智能设备',
roomDeviceUnconfigured: '房间智能设备未配置',
smartDeviceLoadFailed: '智能设备加载失败',
primaryButlerTag: '主责',
noContactPhone: '暂无联系电话',
loadingButlerInfo: '正在同步管家信息...',
noButlerInfo: '暂未获取到管家信息',
managementContacts: '宿管与管理员',
dormitoryManagerTag: '宿管',
counselorTag: '管理员',
noManagerContacts: '暂无管理联系人',
managerContactSummary: '当前区域共有 {count} 位管家可联系',
contactPhonePrefix: '联系电话 ',
loadingShort: '同步中',
onlineSingle: '1人在线',
pendingSync: '待同步',
billEmpty: '暂无待支付账单',
billRecordFallback: '账单记录',
pendingContractFound: '发现待签合同',
statusUpdated: '状态已刷新',
refreshFailed: '刷新失败,请稍后重试',
managerWechatCopied: '管家微信已复制',
noManagerWechat: '暂无管家微信',
loadingManagerInfo: '正在加载管家信息',
managerInitialFallback: '管',
billCount: '账单数量',
billUnit: '笔',
billType: '账单类型',
lastCreateTime: '最近生成',
communityEvent: '近期活动',
joinEvent: '立即报名',
signContractNow: '请尽快签订合同',
signContractDesc: '系统已生成待签合同,当前流程已进入签订合同阶段,完成签约后即可继续后续入住流程。',
contractSignedTitle: '合同已签署,等待办理入住',
contractSignedDesc: '您的住宿合同已经完成签署,管家会尽快为您安排办理入住流程。',
contractPendingNote: '检测到待签合同后,流程将停留在签订合同节点,请先完成签名。',
contractSignedNote: '合同签署完成,请等待管家通知办理入住。',
bookingPendingNote: '订单已提交,管家审核通过后会同步更多入住信息。',
contractRoom: '合同房型',
bookedRoomType: '预定房型',
refreshing: '刷新中...',
signContract: '去签约',
refreshStatus: '刷新状态',
pendingAssignment: '待审核分配',
currentStay: '当前入住房间',
controllable: '可控制',
deviceFallback: '设备 {index}',
bedPrefix: '床位 {label}',
multiButlerName: '{name} 等{count}位管家',
onDutyCount: '{count}人值班',
submitRequest: '提交申请',
completeFormBeforeSubmit: '请完善表单信息后提交。'
},
en: {
title: 'My Stay',
bookingInfo: 'Booking Info',
submitBookingStep: 'Submit',
reviewStep: 'Review',
contractStep: 'Contract',
checkinStep: 'Check-in',
notCheckedInTitle: 'No Leased Room Yet',
notCheckedInDesc: 'We provide premium co-living services.\nPlease explore and find your suite first.',
bookingPendingTitle: 'Your booking is under review',
bookingPendingDesc: 'Deposit has been paid. The booking is waiting for approval and butler assignment.',
goSelectRoom: 'Explore Rooms',
stayService: 'Service Request',
butler: 'Community Butler',
online: 'Online · Response < 5 mins',
callNow: 'Call',
publicService: 'Premium Services',
viewAll: 'View All',
leaseTerm: 'Lease',
activeStatus: 'Active',
myBill: 'Monthly Bill',
unpaid: 'Unpaid',
paidOff: 'Paid Off',
payBill: 'Pay Now',
viewBill: 'View Bills',
smartControl: 'Smart Control',
addSmartDevice: 'Add Smart Device',
smartAvailableAfterCheckin: 'Smart devices are available after check-in',
syncingSmartDevices: 'Syncing smart devices...',
smartDeviceConnected: '{count} smart devices connected',
smartDeviceSyncFailed: 'Smart device sync failed',
smartDeviceEmpty: 'No smart devices in this room',
roomDeviceUnconfigured: 'Smart devices are not configured for this room',
smartDeviceLoadFailed: 'Failed to load smart devices',
primaryButlerTag: 'Lead',
noContactPhone: 'No contact number',
loadingButlerInfo: 'Syncing butler info...',
noButlerInfo: 'No butler information yet',
managementContacts: 'Dorm Managers & Admins',
dormitoryManagerTag: 'Dorm Mgr',
counselorTag: 'Admin',
noManagerContacts: 'No management contacts',
managerContactSummary: '{count} butlers are available in this area',
contactPhonePrefix: 'Phone ',
loadingShort: 'Syncing',
onlineSingle: '1 online',
pendingSync: 'Pending',
billEmpty: 'No unpaid bills',
billRecordFallback: 'Bill',
pendingContractFound: 'Pending contract found',
statusUpdated: 'Status updated',
refreshFailed: 'Refresh failed, please try again later',
managerWechatCopied: 'Butler Wechat copied',
noManagerWechat: 'No butler Wechat',
loadingManagerInfo: 'Loading butler info',
managerInitialFallback: 'B',
billCount: 'Bills',
billUnit: '',
billType: 'Type',
lastCreateTime: 'Latest',
communityEvent: 'Community Events',
joinEvent: 'Join Now',
signContractNow: 'Please sign your contract',
signContractDesc: 'A contract is ready for signature. You are now at the contract signing step.',
contractSignedTitle: 'Contract Signed',
contractSignedDesc: 'Your stay contract has been signed. The butler will arrange check-in for you shortly.',
contractPendingNote: 'The flow stays at the contract step until the contract is signed.',
contractSignedNote: 'Contract signed successfully. Awaiting check-in arrangement.',
bookingPendingNote: 'Your booking has been submitted. More stay details will appear after review.',
contractRoom: 'Contract Room',
bookedRoomType: 'Room Type',
refreshing: 'Refreshing...',
signContract: 'Sign Contract',
refreshStatus: 'Refresh Status',
pendingAssignment: 'Pending assignment',
currentStay: 'Current Stay',
controllable: 'Controllable',
deviceFallback: 'Device {index}',
bedPrefix: 'Bed {label}',
multiButlerName: '{name} +{count}',
onDutyCount: '{count} on duty',
submitRequest: 'Submit Request',
completeFormBeforeSubmit: 'Please complete the form before submitting.'
}
}
}
},
computed: {
@@ -780,42 +610,42 @@ export default {
},
pendingStageTitle() {
if (this.hasSignedContract) {
return this.t('contractSignedTitle')
return this.$tr('合同已签署,等待办理入住')
}
if (this.isContractSigningStep) {
return this.t('signContractNow')
return this.$tr('请尽快签订合同')
}
return this.t('bookingPendingTitle')
return this.$tr('您的预订正在审核中')
},
pendingStageDesc() {
if (this.hasSignedContract) {
return this.t('contractSignedDesc')
return this.$tr('您的住宿合同已经完成签署,管家会尽快为您安排办理入住流程。')
}
if (this.isContractSigningStep) {
return this.t('signContractDesc')
return this.$tr('系统已生成待签合同,当前流程已进入签订合同阶段,完成签约后即可继续后续入住流程。')
}
return this.t('bookingPendingDesc')
return this.$tr('定金已支付,预定单正在审批流中。审批通过后将分配管家并等待分房号。')
},
pendingInfoLabel() {
return this.hasSignedContract || this.isContractSigningStep ? this.t('contractRoom') : this.t('bookedRoomType')
return this.hasSignedContract || this.isContractSigningStep ? this.$tr('合同房型') : this.$tr('预定房型')
},
pendingStageNote() {
if (this.hasSignedContract) {
return this.t('contractSignedNote')
return this.$tr('合同签署完成,请等待管家通知办理入住。')
}
if (this.isContractSigningStep) {
return this.t('contractPendingNote')
return this.$tr('检测到待签合同后,流程将停留在签订合同节点,请先完成签名。')
}
return this.t('bookingPendingNote')
return this.$tr('订单已提交,管家审核通过后会同步更多入住信息。')
},
pendingPrimaryActionText() {
if (this.isPendingStatusRefreshing) {
return this.t('refreshing')
return this.$tr('刷新中...')
}
if (this.isContractSigningStep && !this.hasSignedContract) {
return this.t('signContract')
return this.$tr('去签约')
}
return this.t('refreshStatus')
return this.$tr('刷新状态')
},
bookingRoomText() {
if (!this.currentBooking) {
@@ -824,18 +654,18 @@ export default {
return this.currentBooking.roomName
|| this.currentBooking.roomTypeName
|| this.currentBooking.roomTypeNameEn
|| this.t('pendingAssignment')
|| this.$tr('待审核分配')
},
roomCardCover() {
return this.myRoomData && this.myRoomData.image ? this.myRoomData.image : ''
},
roomTitleText() {
if (!this.myRoomData) {
return this.t('currentStay')
return this.$tr('当前入住房间')
}
return this.language === 'zh'
? (pickReadableValue(this.myRoomData.titleZh, this.myRoomData.roomTypeName, this.myRoomData.roomName, this.myRoomData.titleEn) || this.t('currentStay'))
: (pickReadableValue(this.myRoomData.titleEn, this.myRoomData.roomTypeNameEn, this.myRoomData.roomTypeName, this.myRoomData.titleZh) || this.t('currentStay'))
? (pickReadableValue(this.myRoomData.titleZh, this.myRoomData.roomTypeName, this.myRoomData.roomName, this.myRoomData.titleEn) || this.$tr('当前入住房间'))
: (pickReadableValue(this.myRoomData.titleEn, this.myRoomData.roomTypeNameEn, this.myRoomData.roomTypeName, this.myRoomData.titleZh) || this.$tr('当前入住房间'))
},
roomDisplayText() {
if (!this.myRoomData) {
@@ -866,10 +696,10 @@ export default {
},
unpaidBillPrimaryType() {
if (!this.unpaidBillList.length) {
return this.t('billEmpty')
return this.$tr('暂无待支付账单')
}
const firstBill = this.unpaidBillList[0] || {}
return firstBill.referenceTypeDesc || getAccountReferenceTypeLabel(firstBill.referenceType, this.t('billRecordFallback'))
return firstBill.referenceTypeDesc || getAccountReferenceTypeLabel(firstBill.referenceType, this.$tr('账单记录'))
},
latestBillTimeText() {
if (!this.unpaidBillList.length || !this.unpaidBillList[0].createTime) {
@@ -879,25 +709,25 @@ export default {
},
smartSummaryText() {
if (!this.hasCheckedIn) {
return this.t('smartAvailableAfterCheckin')
return this.$tr('入住后可使用智能设备')
}
if (this.isSmartDevicesLoading) {
return this.t('syncingSmartDevices')
return this.$tr('正在同步智能设备...')
}
if (this.smartDevices.length) {
return this.t('smartDeviceConnected').replace('{count}', this.smartDevices.length)
return this.$tr('已接入 {count} 台智能设备').replace('{count}', this.smartDevices.length)
}
if (this.smartDeviceError) {
return this.t('smartDeviceSyncFailed')
return this.$tr('智能设备同步失败')
}
return this.t('smartDeviceEmpty')
return this.$tr('当前房间暂无智能设备')
},
smartQuickDeviceCards() {
return this.smartDevices.map((device, index) => ({
...device,
key: device.renderKey || device.deviceId || device.deviceNo || `smart-device-${index}`,
name: device.deviceName || this.t('deviceFallback').replace('{index}', index + 1),
stateText: device.stateText || this.t('controllable')
name: device.deviceName || this.$tr('设备 {index}').replace('{index}', index + 1),
stateText: device.stateText || this.$tr('可控制')
}))
},
stayLocationText() {
@@ -910,7 +740,7 @@ export default {
this.myRoomData.buildingName || '',
this.myRoomData.buildingSectionName || '',
this.myRoomData.floorName || this.myRoomData.floor || '',
bedLabel ? this.t('bedPrefix').replace('{label}', bedLabel) : ''
bedLabel ? this.$tr('床位 {label}').replace('{label}', bedLabel) : ''
].filter(Boolean).join(' · ')
},
butlerAvatar() {
@@ -930,10 +760,10 @@ export default {
const manager = this.primaryAreaManager || {}
const managerName = manager.displayName || manager.name || manager.userName || manager.nickname || manager.userCode || ''
if (!managerName) {
return this.t('butler')
return this.$tr('专属社区管家')
}
if (this.areaManagers.length > 1) {
return this.t('multiButlerName')
return this.$tr('{name} 等{count}位管家')
.replace('{name}', managerName)
.replace('{count}', this.language === 'zh' ? this.areaManagers.length : this.areaManagers.length - 1)
}
@@ -941,29 +771,29 @@ export default {
},
butlerSubtitle() {
if (this.isAreaManagerLoading) {
return this.t('loadingButlerInfo')
return this.$tr('正在同步管家信息...')
}
if (this.areaManagers.length > 1) {
return this.t('managerContactSummary').replace('{count}', this.areaManagers.length)
return this.$tr('当前区域共有 {count} 位管家可联系').replace('{count}', this.areaManagers.length)
}
const manager = this.primaryAreaManager || {}
const phone = this.getManagerPhone(manager)
if (phone) {
return `${this.t('contactPhonePrefix')}${phone}`
return `${this.$tr('联系电话 ')}${phone}`
}
return this.t('online')
return this.$tr('当前在线 · 响应时间 < 5分钟')
},
butlerCountText() {
if (this.isAreaManagerLoading) {
return this.t('loadingShort')
return this.$tr('同步中')
}
if (this.areaManagers.length > 1) {
return this.t('onDutyCount').replace('{count}', this.areaManagers.length)
return this.$tr('{count}人值班').replace('{count}', this.areaManagers.length)
}
if (this.areaManagers.length === 1) {
return this.t('onlineSingle')
return this.$tr('1人在线')
}
return this.t('pendingSync')
return this.$tr('待同步')
},
butlerPhoneNumber() {
const manager = this.primaryAreaManager || {}
@@ -995,9 +825,6 @@ export default {
this.loadUnpaidBills()
},
methods: {
t(key, params) {
return this.$tm('pages.index.component.StayServiceTab', key, params)
},
pickItemText(item = {}, zhField, enField, fallback = '') {
if (this.language === 'en') {
const shortField = `${enField}Short`
@@ -1139,7 +966,7 @@ export default {
const roomCode = this.getCurrentSmartRoomCode()
if (!brandId || !roomCode) {
this.resetSmartDevicesState()
this.smartDeviceError = this.t('roomDeviceUnconfigured')
this.smartDeviceError = this.$tr('房间智能设备未配置')
return Promise.resolve()
}
@@ -1162,7 +989,7 @@ export default {
.catch((error) => {
console.error('load stay smart devices failed', error)
this.smartDevices = []
this.smartDeviceError = (error && (error.message || error.msg)) || this.t('smartDeviceLoadFailed')
this.smartDeviceError = (error && (error.message || error.msg)) || this.$tr('智能设备加载失败')
})
.finally(() => {
this.isSmartDevicesLoading = false
@@ -1397,15 +1224,15 @@ export default {
})
uni.showToast({
title: pendingContracts.length
? this.t('pendingContractFound')
: this.t('statusUpdated'),
? this.$tr('发现待签合同')
: this.$tr('状态已刷新'),
icon: 'success'
})
})
.catch((error) => {
console.error('refresh pending status failed', error)
uni.showToast({
title: this.t('refreshFailed'),
title: this.$tr('刷新失败,请稍后重试'),
icon: 'none'
})
})
@@ -1430,10 +1257,10 @@ export default {
},
buildServiceSubmitUrl(item = {}) {
const roomData = this.myRoomData || {}
const titleZh = item.title || item.titleEn || item.nameZh || item.nameEn || this.t('submitRequest')
const titleEn = item.titleEnShort || item.titleEn || item.title || item.nameEn || item.nameZh || this.t('submitRequest')
const descZh = item.desc || item.descEn || this.t('completeFormBeforeSubmit')
const descEn = compactEnglishText(item.descEn || item.desc || '', 68) || item.descEn || item.desc || this.t('completeFormBeforeSubmit')
const titleZh = item.title || item.titleEn || item.nameZh || item.nameEn || this.$tr('提交申请')
const titleEn = item.titleEnShort || item.titleEn || item.title || item.nameEn || item.nameZh || this.$tr('提交申请')
const descZh = item.desc || item.descEn || this.$tr('请完善表单信息后提交。')
const descEn = compactEnglishText(item.descEn || item.desc || '', 68) || item.descEn || item.desc || this.$tr('请完善表单信息后提交。')
const title = this.language === 'en'
? titleEn
: titleZh
@@ -1451,7 +1278,7 @@ export default {
roomTypeName: item.key === 'change-room' ? (roomData.roomTypeName || roomData.titleZh || '') : '',
roomName: roomData.roomLabel || ''
}
return `/pkgService/submit_request/submit_request?title=${encodeURIComponent(title || this.t('submitRequest'))}&titleZh=${encodeURIComponent(titleZh)}&titleEn=${encodeURIComponent(titleEn)}&desc=${encodeURIComponent(desc || this.t('completeFormBeforeSubmit'))}&descZh=${encodeURIComponent(descZh)}&descEn=${encodeURIComponent(descEn)}&payload=${encodeURIComponent(JSON.stringify(payload))}`
return `/pkgService/submit_request/submit_request?title=${encodeURIComponent(title || this.$tr('提交申请'))}&titleZh=${encodeURIComponent(titleZh)}&titleEn=${encodeURIComponent(titleEn)}&desc=${encodeURIComponent(desc || this.$tr('请完善表单信息后提交。'))}&descZh=${encodeURIComponent(descZh)}&descEn=${encodeURIComponent(descEn)}&payload=${encodeURIComponent(JSON.stringify(payload))}`
},
buildTicketWorkbenchUrl(item = {}) {
const serviceTypeMap = {
@@ -1506,7 +1333,7 @@ export default {
fail: (error) => {
console.error('跳转送餐小程序失败', error)
uni.showToast({
title: this.t('mealDeliveryJumpFailed'),
title: this.$tr('跳转送餐小程序失败'),
icon: 'none'
})
}
@@ -1523,7 +1350,7 @@ export default {
},
getManagerInitial(manager = {}) {
const name = this.getManagerName(manager)
return String(name).trim().slice(0, 1) || this.t('managerInitialFallback')
return String(name).trim().slice(0, 1) || this.$tr('管')
},
handleButlerWechat() {
const manager = this.primaryAreaManager || {}
@@ -1533,7 +1360,7 @@ export default {
data: String(wechat),
success: () => {
uni.showToast({
title: this.t('managerWechatCopied'),
title: this.$tr('管家微信已复制'),
icon: 'success'
})
}
@@ -1541,7 +1368,7 @@ export default {
return
}
uni.showToast({
title: this.t('noManagerWechat'),
title: this.$tr('暂无管家微信'),
icon: 'none'
})
},
@@ -1549,7 +1376,7 @@ export default {
const phone = this.getManagerPhone(manager)
if (!phone) {
uni.showToast({
title: this.t('noContactPhone'),
title: this.$tr('暂无联系电话'),
icon: 'none'
})
return
@@ -1561,7 +1388,7 @@ export default {
handleButlerCall() {
if (!this.butlerPhoneNumber) {
uni.showToast({
title: this.isAreaManagerLoading ? this.t('loadingManagerInfo') : this.t('noContactPhone'),
title: this.isAreaManagerLoading ? this.$tr('正在加载管家信息') : this.$tr('暂无联系电话'),
icon: 'none'
})
return