Files
QSY-lvjuxcx/pkgAccount/auth_result/auth_result.vue
liangjian 6e721ccc7e refactor: 国际化文本迁移至全局 locales/messages.js
将组件级 i18n 对象重构为统一由全局 locales/messages.js 管理,确保中英文语言切换时正确显示对应文本。

- 新增 utils/i18n.js 国际化工具函数
- 更新 store/$t.mixin.js 翻译混入逻辑
- 迁移 pkgService、pkgBooking、pkgAccount、pkgExplore、pages 等共 58 个组件的中英文文本至全局消息表
2026-07-24 13:43:41 +08:00

553 lines
16 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="auth-result-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 class="status-card fade-in">
<!-- 状态大图标 -->
<view class="status-icon-wrapper tn-flex tn-flex-row-center tn-flex-col-center" :class="authStatus">
<text v-if="authStatus === 'approved'" class="tn-icon-success-circle-fill status-icon icon-success"></text>
<text v-else-if="authStatus === 'rejected'" class="tn-icon-close-circle-fill status-icon icon-rejected"></text>
<text v-else-if="authStatus === 'pending'" class="tn-icon-time-fill status-icon icon-pending"></text>
<text v-else class="tn-icon-warning-fill status-icon icon-unsubmitted"></text>
</view>
<!-- 状态标题与描述 -->
<view class="tn-text-center tn-margin-top-lg tn-padding-left-lg tn-padding-right-lg">
<view class="card-title">{{ titleText }}</view>
<!-- <view class="card-desc tn-margin-top-sm">{{ descText }}</view> -->
</view>
<!-- <view v-if="applicationCode" class="code-chip tn-margin-top-lg">
申请编号:{{ applicationCode }}
</view> -->
<!-- 审批备注 -->
<view v-if="approvalRemark" class="reason-box tn-margin-top-xl tn-margin-left-lg tn-margin-right-lg" :class="authStatus">
<view class="reason-title tn-flex tn-flex-row-center tn-flex-col-center">
<text :class="remarkIconClass" class="tn-margin-right-xs"></text>{{ approvalRemarkTitle }}
</view>
<view class="reason-content tn-margin-top-sm tn-text-center">{{ approvalRemark }}</view>
</view>
<!-- 提交信息摘要 -->
<view v-if="profileSummary.length" class="summary-box tn-margin-top-xl tn-margin-bottom-lg tn-margin-left-lg tn-margin-right-lg">
<view class="summary-title tn-margin-bottom-sm">{{ $tr('已提交信息') }}</view>
<view v-for="item in profileSummary" :key="item.label" class="summary-row tn-flex tn-flex-row-between tn-padding-top-sm tn-padding-bottom-sm">
<text class="summary-label">{{ item.label }}</text>
<text class="summary-value tn-text-ellipsis">{{ item.value }}</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-group tn-margin-top-xl fade-in-up">
<view v-if="authStatus === 'approved'" class="custom-btn btn-primary" @click="goToLogin">
<text class="tn-text-bold">{{ $tr('去登录') }}</text>
</view>
<view v-else-if="authStatus === 'rejected'" class="custom-btn btn-primary" @click="resubmit">
<text class="tn-text-bold">{{ $tr('重新提交注册申请') }}</text>
</view>
<view v-else class="custom-btn btn-dark" @click="refreshStatus">
<text class="tn-text-bold tn-flex tn-flex-row-center tn-flex-col-center">
<text v-if="loading" class="tn-icon-loading tn-margin-right-xs spin"></text>
{{ loading ? $tr('刷新中...') : $tr('刷新审批状态') }}
</text>
</view>
<view class="custom-btn btn-light tn-margin-top" @click="goToRegistrationRecords">
<text class="tn-text-bold">{{ $tr('查看注册记录') }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { saveRegisteredUser } from '../../utils/app-state'
import { registrationApplicationMePage, registrationMyorder } from '@/api/index.js'
import { fetchEnums, getApprovalStatusLabel } from '../../utils/enum-store'
import { buildUserFromRegistrationApplication, mapRegistrationApprovalToAuthStatus, resolveRegistrationApplicationCode } from '../../utils/flow'
function formatPageResponse(response) {
const payload = response && response.data ? response.data : response
return payload && typeof payload === 'object' ? payload : {}
}
export default {
data() {
return {
loading: false,
applicationInfo: null,
targetCode: '',
sourceType: ''
}
},
computed: {
language() {
return this.$getLanguage()
},
authStatus() {
const status = this.applicationInfo ? this.applicationInfo.approvalStatus : undefined
return mapRegistrationApprovalToAuthStatus(status)
},
applicationCode() {
return this.targetCode || resolveRegistrationApplicationCode(this.applicationInfo) || ''
},
approvalStatusText() {
const status = this.applicationInfo ? this.applicationInfo.approvalStatus : undefined
if (typeof status === 'number') {
return this.getApprovalStatusLabelLocal(status)
}
return this.$tr('未提交')
},
titleText() {
const map = {
unsubmitted: this.$tr('您还未提交注册申请'),
pending: this.$tr('注册申请已提交'),
approved: this.$tr('注册审批已通过'),
rejected: this.$tr('注册审批未通过')
}
return map[this.authStatus] || map.unsubmitted
},
descText() {
const map = {
unsubmitted: this.$tr('请先提交注册申请单,审批通过后才算注册成功。'),
pending: this.sourceType === 'registration-records'
? this.$tr('当前展示的是该申请单的审批状态,可下拉或点击按钮刷新最新结果。')
: this.$tr('当前申请正在审批中,审批通过前无法登录。'),
approved: this.$tr('您的注册申请已审批通过,现在可以前往登录。'),
rejected: this.sourceType === 'registration-records'
? this.$tr('当前展示的是该申请单的审批结果,请根据驳回原因修改信息后重新提交注册申请。')
: this.$tr('请根据驳回原因修改信息后重新提交注册申请。')
}
return map[this.authStatus] || map.unsubmitted
},
approvalRemarkTitle() {
const map = {
approved: this.$tr('通过备注'),
rejected: this.$tr('驳回原因'),
pending: this.$tr('审批备注'),
unsubmitted: this.$tr('审批备注')
}
return map[this.authStatus] || map.pending
},
approvalRemark() {
return this.applicationInfo && this.applicationInfo.approveReason
? this.applicationInfo.approveReason
: ''
},
remarkIconClass() {
const map = {
approved: 'tn-icon-success',
rejected: 'tn-icon-warning',
pending: 'tn-icon-time',
unsubmitted: 'tn-icon-warning'
}
return map[this.authStatus] || map.pending
},
profileSummary() {
const profile = this.applicationInfo || {}
const identityType = profile.passportNumber ? 'foreign' : 'domestic'
const roleText = identityType === 'foreign' ? this.$tr('外籍人员') : identityType === 'domestic' ? this.$tr('中国籍') : this.$tr('-')
const certificateNo = profile.passportNumber || profile.idCard || this.$tr('-')
return [
{ label: this.$tr('人员类型'), value: roleText },
{ label: this.$tr('姓名'), value: profile.name || this.$tr('-') },
{ label: this.$tr('注册账号'), value: profile.account || this.$tr('-') },
{ label: this.$tr('证件号'), value: certificateNo },
// { label: '国籍', value: profile.country || profile.nationality || '-' },
// { label: '工作单位', value: profile.workUnit || '-' },
// { label: '兴趣爱好', value: hobbyText }
]
}
},
watch: {
language() {
this.refreshStatus()
}
},
onLoad(options = {}) {
this.sourceType = options.from || ''
this.targetCode = decodeURIComponent(options.code || '').trim()
const payloadText = options.payload ? decodeURIComponent(options.payload) : ''
if (!payloadText) {
return
}
try {
const payload = JSON.parse(payloadText)
if (payload && typeof payload === 'object') {
this.applicationInfo = payload
if (!this.targetCode) {
this.targetCode = resolveRegistrationApplicationCode(payload)
}
}
} catch (error) {
console.error('parse auth result payload failed', error)
}
},
async onShow() {
await fetchEnums(false, this.language)
this.refreshStatus()
},
methods: {
getWechatCode() {
return new Promise((resolve) => {
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
success: (res) => {
resolve(res.code || '')
},
fail: (err) => {
console.error('getWechatCode failed', err)
resolve('')
}
})
// #endif
// #ifndef MP-WEIXIN
resolve('')
// #endif
})
},
async fetchApplicationByCode(providerValue, applicationCode) {
if (!providerValue || !applicationCode) {
return null
}
const response = await registrationApplicationMePage({
ProviderValue: providerValue,
Search: applicationCode,
PageIndex: 1,
PageSize: 20
}, this.language)
const pageData = formatPageResponse(response)
const list = Array.isArray(pageData.data) ? pageData.data : []
return list.find(item => resolveRegistrationApplicationCode(item) === applicationCode) || list[0] || null
},
async refreshStatus() {
this.loading = true
try {
const providerValue = await this.getWechatCode()
const applicationCode = this.applicationCode
let target = null
if (applicationCode) {
target = await this.fetchApplicationByCode(providerValue || '', applicationCode)
} else {
const response = await registrationMyorder({
providerValue: providerValue || ''
}, this.language)
const rawTarget = response && response.data && typeof response.data === 'object' ? response.data : response
target = rawTarget.data !== undefined ? rawTarget.data : rawTarget
}
if (!target || typeof target !== 'object' || !Object.keys(target).length) {
if (this.applicationInfo && this.applicationCode) {
uni.showToast({
title: this.$tr('未查到最新详情,已展示列表记录'),
icon: 'none'
})
return
}
this.applicationInfo = {
approvalStatus: 1
}
uni.showToast({
title: this.$tr('审批已通过,请登录'),
icon: 'success'
})
setTimeout(() => {
this.goToLogin()
}, 800)
return
}
this.applicationInfo = target
this.targetCode = resolveRegistrationApplicationCode(target) || this.targetCode
if (target.approvalStatus === 1) {
const approvedUser = buildUserFromRegistrationApplication(target, {
password: ''
})
saveRegisteredUser(approvedUser)
}
} catch (error) {
console.error('refresh registration status failed', error)
uni.showToast({
title: this.$tr('获取审批状态失败'),
icon: 'none'
})
} finally {
this.loading = false
}
},
getApprovalStatusLabelLocal(status) {
return getApprovalStatusLabel(
status,
this.authStatus === 'pending'
? this.$tr('审核中')
: this.authStatus === 'approved'
? this.$tr('已通过')
: this.authStatus === 'rejected'
? this.$tr('已驳回')
: this.$tr('未提交'),
this.language
)
},
goToLogin() {
uni.reLaunch({
url: '/pkgAccount/login/login?bypass=1'
})
},
resubmit() {
uni.redirectTo({
url: '/pkgAccount/register/register?bypass=1'
})
},
goToRegistrationRecords() {
uni.navigateTo({
url: '/pkgAccount/registration_records/registration_records'
})
},
}
}
</script>
<style lang="scss" scoped>
.auth-result-page {
/* 白蓝主题渐变背景 */
background: linear-gradient(180deg, #F0F6FF 0%, #F8FAFF 100%);
min-height: 100vh;
}
.status-card {
background: #ffffff;
border-radius: 24rpx;
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.04);
border: 1rpx solid rgba($brand-primary, 0.05);
position: relative;
z-index: 1;
margin-top: 120rpx;
padding-bottom: 40rpx;
}
.status-icon-wrapper {
width: 140rpx;
height: 140rpx;
margin: 0 auto;
border-radius: 50%;
margin-top: -70rpx;
background: #ffffff;
box-shadow: 0 8rpx 24rpx rgba($brand-primary, 0.08);
}
.status-icon-wrapper.approved {
background: rgba($brand-primary, 0.1);
}
.status-icon-wrapper.rejected {
background: rgba(239, 68, 68, 0.1);
}
.status-icon-wrapper.pending,
.status-icon-wrapper.unsubmitted {
background: rgba(245, 158, 11, 0.12);
}
.status-icon {
font-size: 80rpx;
}
.icon-success {
color: $brand-primary;
}
.icon-rejected {
color: #cf3c3c;
}
.icon-pending,
.icon-unsubmitted {
color: #c98512;
}
.card-title {
font-size: 44rpx;
font-weight: bold;
color: #1f1f1f;
}
.card-desc {
font-size: 28rpx;
color: #888888;
line-height: 1.6;
}
.code-chip {
width: fit-content;
max-width: calc(100% - 64rpx);
margin-left: auto;
margin-right: auto;
padding: 12rpx 24rpx;
border-radius: 999rpx;
background: rgba($brand-primary, 0.08);
color: $brand-primary;
font-size: 24rpx;
font-weight: 600;
}
.reason-box {
padding: 30rpx;
border-radius: 20rpx;
}
.reason-box.approved {
background: rgba($brand-primary, 0.05);
border: 1rpx solid rgba($brand-primary, 0.1);
}
.reason-box.approved .reason-title,
.reason-box.approved .reason-content {
color: $brand-primary;
}
.reason-box.rejected {
background: rgba(239, 68, 68, 0.05);
border: 1rpx solid rgba(239, 68, 68, 0.1);
}
.reason-box.rejected .reason-title,
.reason-box.rejected .reason-content {
color: #ef4444;
}
.reason-box.pending,
.reason-box.unsubmitted {
background: rgba(245, 158, 11, 0.05);
border: 1rpx solid rgba(245, 158, 11, 0.1);
}
.reason-box.pending .reason-title,
.reason-box.pending .reason-content,
.reason-box.unsubmitted .reason-title,
.reason-box.unsubmitted .reason-content {
color: #f59e0b;
}
.reason-title {
font-size: 28rpx;
font-weight: bold;
}
.reason-content {
font-size: 26rpx;
line-height: 1.6;
}
.summary-box {
padding: 30rpx;
background: rgba($brand-primary, 0.02);
border-radius: 20rpx;
border: 1rpx solid rgba($brand-primary, 0.1);
}
.summary-title {
font-size: 30rpx;
font-weight: bold;
color: #1A2233;
}
.summary-row {
border-bottom: 1rpx dashed rgba($brand-primary, 0.15);
}
.summary-row:last-child {
border-bottom: none;
}
.summary-label {
font-size: 28rpx;
color: #6A7A99;
}
.summary-value {
font-size: 28rpx;
color: #1A2233;
font-weight: 500;
max-width: 65%;
text-align: right;
}
.custom-btn {
width: 100%;
height: 96rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 999rpx;
font-size: 32rpx;
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);
}
.btn-dark {
background: #ffffff;
color: $brand-primary;
border: 1rpx solid rgba($brand-primary, 0.3);
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.05);
}
.btn-light {
background: #ffffff;
color: #1A2233;
border: 1rpx solid rgba($brand-primary, 0.15);
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.04);
}
/* 动画效果 */
.fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out forwards;
animation-delay: 0.2s;
opacity: 0;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(40rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.spin {
display: inline-block;
animation: spin 1s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
</style>