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

@@ -7,7 +7,7 @@
<!-- Header -->
<view class="page-header" :style="{ paddingTop: vuex_custom_bar_height ? 'calc(' + vuex_custom_bar_height + 'px + 32rpx)' : '32rpx' }">
<view class="title-wrap">
<text class="title-zh">{{ t('pageTitle') }}</text>
<text class="title-zh">{{ $tr('验房') }}</text>
<text class="title-en">Inspection</text>
</view>
<view v-if="activeInspection" class="status-tag">
@@ -37,15 +37,15 @@
<!-- High Density Info Grid -->
<view class="info-grid">
<view class="info-cell">
<text class="info-label">{{ t('roomNoLabel') }}</text>
<text class="info-label">{{ $tr('房间号') }}</text>
<text class="info-value font-mono">{{ activeInspectionDetail.roomNo || '--' }}</text>
</view>
<view class="info-cell">
<text class="info-label">{{ t('inspectionTypeLabel') }}</text>
<text class="info-label">{{ $tr('验房类型') }}</text>
<text class="info-value">{{ activeInspectionDetail.typeText }}</text>
</view>
<view class="info-cell full-width">
<text class="info-label">{{ t('inspectionTimeLabel') }}</text>
<text class="info-label">{{ $tr('发起时间') }}</text>
<text class="info-value font-mono">{{ activeInspectionDetail.createTimeText }}</text>
</view>
</view>
@@ -55,7 +55,7 @@
<textarea
class="remarks-input"
:value="remarksDraft"
:placeholder="t('inspectionRemarksPlaceholder')"
:placeholder="$tr('可填写本次验房的补充说明')"
maxlength="300"
auto-height
@input="handleRemarksInput"
@@ -65,7 +65,7 @@
<!-- Items -->
<view v-if="activeInspectionDetail.items.length" class="items-wrap">
<view class="section-title">
<text class="section-title-zh">{{ t('inspectionItemsTitle') }}</text>
<text class="section-title-zh">{{ $tr('检查项') }}</text>
<text class="section-title-en">Items</text>
</view>
@@ -77,34 +77,34 @@
>
<view class="item-header">
<view class="item-title-col">
<text class="item-name">{{ item.deviceName || t('deviceFallback') }}</text>
<text class="item-name">{{ item.deviceName || $tr('未命名设备') }}</text>
<text class="item-meta" v-if="item.assetSourceText || item.brandSnapshot">
{{ [item.assetSourceText, item.brandSnapshot].filter(Boolean).join(' / ') }}
</text>
</view>
<text v-if="item.isRequiredSnapshot" class="item-required">{{ t('requiredTag') }}</text>
<text v-if="item.isRequiredSnapshot" class="item-required">{{ $tr('必查') }}</text>
</view>
<view class="item-body">
<view class="row-flex">
<view class="col-half">
<text class="field-label">{{ t('expectedQuantityLabel') }}</text>
<text class="field-label">{{ $tr('应有数量') }}</text>
<text class="field-value font-mono">{{ item.expectedQuantityText }}</text>
</view>
<view class="col-half">
<text class="field-label">{{ t('checkedQuantityLabel') }}</text>
<text class="field-label">{{ $tr('实测数量') }}</text>
<input
class="field-input font-mono"
type="number"
:value="item.checkedQuantityDraft"
:placeholder="t('checkedQuantityPlaceholder')"
:placeholder="$tr('请输入数量')"
@input="updateItemField(index, 'checkedQuantityDraft', $event.detail.value)"
/>
</view>
</view>
<view class="field-row mt-20">
<text class="field-label">{{ t('inspectionResultLabel') }}</text>
<text class="field-label">{{ $tr('检查结果') }}</text>
<view class="radio-group">
<view
v-for="option in inspectionResultOptions"
@@ -122,7 +122,7 @@
<textarea
class="field-textarea"
:value="item.problemDetailDraft"
:placeholder="t('problemDetailPlaceholder')"
:placeholder="$tr('请描述异常、缺失或损坏情况')"
maxlength="300"
auto-height
@input="updateItemField(index, 'problemDetailDraft', $event.detail.value)"
@@ -155,25 +155,25 @@
</view>
<view v-else-if="!loading" class="empty-state">
<text class="empty-title">{{ t('emptyItemsTitle') }}</text>
<text class="empty-desc">{{ t('emptyItemsDesc') }}</text>
<text class="empty-title">{{ $tr('暂无检查项') }}</text>
<text class="empty-desc">{{ $tr('当前验房单还没有检查项明细。') }}</text>
</view>
</view>
<view v-if="!loading && !inspectionList.length" class="empty-state">
<text class="empty-title">{{ t('emptyTitle') }}</text>
<text class="empty-desc">{{ t('emptyDesc') }}</text>
<text class="empty-title">{{ $tr('暂无验房单') }}</text>
<text class="empty-desc">{{ $tr('当前订单还没有可查看的验房记录。') }}</text>
</view>
<view v-if="activeInspectionDetail" class="bottom-action">
<view class="btn-submit" @click="submitInspection">
{{ submitting ? t('submitting') : t('submitAction') }}
{{ submitting ? $tr('提交中...') : $tr('提交验房') }}
</view>
</view>
<view v-if="loading" class="global-loading">
<tn-loading mode="circle" color="#0F172A" size="48"></tn-loading>
<text class="loading-text">{{ t('loadingDesc') }}</text>
<text class="loading-text">{{ $tr('正在同步验房数据...') }}</text>
</view>
</view>
</template>
@@ -295,84 +295,6 @@ export default {
})
},
methods: {
t(key) {
const map = {
zh: {
pageTitle: '验房',
pageDesc: '按当前订单查看并提交验房结果',
inspectionListTitle: '验房记录',
roomNoLabel: '房间号',
inspectionTypeLabel: '验房类型',
inspectionStatusLabel: '当前状态',
inspectionTimeLabel: '发起时间',
inspectionRemarksLabel: '备注',
inspectionRemarksPlaceholder: '可填写本次验房的补充说明',
inspectionItemsTitle: '检查项',
inspectionItemsDesc: '请根据现场情况逐项核对',
deviceFallback: '未命名设备',
requiredTag: '必查',
expectedQuantityLabel: '应有数量',
inspectionResultLabel: '检查结果',
checkedQuantityLabel: '实测数量',
checkedQuantityPlaceholder: '请输入数量',
problemDetailLabel: '问题说明',
problemDetailPlaceholder: '请描述异常、缺失或损坏情况',
photosLabel: '现场照片',
addPhoto: '添加',
submitAction: '提交验房',
loadingDesc: '正在同步验房数据...',
emptyTitle: '暂无验房单',
emptyDesc: '当前订单还没有可查看的验房记录。',
emptyItemsTitle: '暂无检查项',
emptyItemsDesc: '当前验房单还没有检查项明细。',
submitSuccess: '验房单已提交',
submitFailed: '提交失败',
submitting: '提交中...',
missingInspectionNo: '未获取到验房单',
uploadFailed: '图片上传失败',
chooseImageFailed: '选择图片失败',
resultRequired: '请先完成所有检查项结果选择'
},
en: {
pageTitle: 'Inspection',
pageDesc: 'Review and submit inspection results for the current order',
inspectionListTitle: 'Inspection Records',
roomNoLabel: 'Room No.',
inspectionTypeLabel: 'Inspection Type',
inspectionStatusLabel: 'Status',
inspectionTimeLabel: 'Created At',
inspectionRemarksLabel: 'Remarks',
inspectionRemarksPlaceholder: 'Add notes for this inspection',
inspectionItemsTitle: 'Inspection Items',
inspectionItemsDesc: 'Check each item according to the on-site condition',
deviceFallback: 'Unnamed Device',
requiredTag: 'Required',
expectedQuantityLabel: 'Expected Qty',
inspectionResultLabel: 'Result',
checkedQuantityLabel: 'Checked Qty',
checkedQuantityPlaceholder: 'Enter quantity',
problemDetailLabel: 'Problem Detail',
problemDetailPlaceholder: 'Describe any abnormal, missing, or damaged condition',
photosLabel: 'Photos',
addPhoto: 'Add',
submitAction: 'Submit',
loadingDesc: 'Syncing inspection data...',
emptyTitle: 'No inspections',
emptyDesc: 'No inspection record is available for the current order.',
emptyItemsTitle: 'No items',
emptyItemsDesc: 'No inspection item is available for this inspection.',
submitSuccess: 'Inspection submitted',
submitFailed: 'Failed to submit',
submitting: 'Submitting...',
missingInspectionNo: 'Missing inspection record',
uploadFailed: 'Failed to upload image',
chooseImageFailed: 'Failed to choose image',
resultRequired: 'Please complete the result selection for all items first'
}
}
const lang = this.language === 'en' ? 'en' : 'zh'
return (map[lang] && map[lang][key]) || key
},
syncInspectionResultOptions() {
this.inspectionResultOptions = this.getEnumOptionsWithAliases([
'InspectionItemResultEnum',
@@ -561,7 +483,7 @@ export default {
const hasMissingResult = items.some(item => item.result === null || Number.isNaN(item.result))
if (hasMissingResult) {
uni.showToast({
title: this.t('resultRequired'),
title: this.$tr('请先完成所有检查项结果选择'),
icon: 'none'
})
return false
@@ -584,7 +506,7 @@ export default {
items: this.buildSubmitItems()
}, this.language)
uni.showToast({
title: this.t('submitSuccess'),
title: this.$tr('验房单已提交'),
icon: 'success'
})
setTimeout(() => {
@@ -597,7 +519,7 @@ export default {
}, 500)
} catch (error) {
uni.showToast({
title: (error && (error.message || error.msg)) || this.t('submitFailed'),
title: (error && (error.message || error.msg)) || this.$tr('提交失败'),
icon: 'none'
})
} finally {
@@ -615,7 +537,7 @@ export default {
return
}
uni.showLoading({
title: this.t('loadingDesc'),
title: this.$tr('正在同步验房数据...'),
mask: true
})
try {
@@ -631,7 +553,7 @@ export default {
)
const remoteUrl = resolveUploadedFileUrl(uploadResult)
if (!remoteUrl) {
throw new Error(this.t('uploadFailed'))
throw new Error(this.$tr('图片上传失败'))
}
uploadedUrls.push(addFilePrefix(remoteUrl))
}
@@ -643,7 +565,7 @@ export default {
this.$set(detail.items[index], 'imageUrlsDraft', nextUrls)
} catch (error) {
uni.showToast({
title: (error && (error.message || error.msg)) || this.t('uploadFailed'),
title: (error && (error.message || error.msg)) || this.$tr('图片上传失败'),
icon: 'none'
})
} finally {
@@ -652,7 +574,7 @@ export default {
},
fail: () => {
uni.showToast({
title: this.t('chooseImageFailed'),
title: this.$tr('选择图片失败'),
icon: 'none'
})
}