Files
pda-overseas-uniapp/src/pages/profile/profile.vue
T
2026-11-03 17:15:11 +08:00

127 lines
8.5 KiB
Vue
Raw 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 v-if="user" class="page-shell portal-page">
<PortalHeader title="个人中心" :user="user" :show-profile="false" />
<view class="portal-content profile-content">
<view class="profile-card"><view class="profile-avatar">{{ avatar }}</view><view><text class="profile-name">{{ displayName }}</text><text class="profile-role">{{ roleLabel }}</text></view></view>
<view class="profile-list">
<view class="profile-row"><text>作业仓库</text><text class="profile-value">{{ warehouseDisplay }}</text></view>
<view class="profile-row"><text>当前班次</text><text class="profile-value">以海外仓排班为准</text></view>
<view class="profile-row"><text>应用版本</text><text class="profile-value">{{ appVersion }}</text></view>
<button class="profile-row profile-row-action" @tap="checkUpdate" :disabled="checkingUpdate"><text>{{ checkingUpdate ? '检查中…' : '检查更新' }}</text><text class="profile-row-arrow">›</text></button>
<button class="profile-row profile-row-action" @tap="showSettings"><text>系统设置</text><text class="profile-row-arrow">›</text></button>
<button class="profile-row profile-row-action" @tap="selectLanguage"><text>切换语言</text><view class="profile-row-end"><text class="profile-value">{{ languageLabel }}</text><text class="profile-row-arrow">›</text></view></button>
</view>
<button class="logout-action" @tap="openLogout">退出登录</button>
</view>
<uni-popup ref="languagePopup" type="bottom" background-color="transparent" :is-mask-click="true" @change="handleLanguageChange">
<view class="language-sheet" role="dialog" aria-modal="true" aria-label="选择语言">
<PopupHeader title="选择语言" @close="closeLanguage" />
<view class="language-options" role="radiogroup" aria-label="可选语言">
<button v-for="item in languageOptions" :key="item.value" class="language-option" :class="{ selected: locale === item.value }" role="radio" :aria-checked="locale === item.value" @tap="applyLanguage(item.value)">
<text class="language-option-name">{{ item.label }}</text>
<view v-if="locale === item.value" class="language-option-current"><text>已选择</text><uni-icons type="checkmarkempty" color="#135ee8" size="20" /></view>
</button>
</view>
<text class="language-sheet-hint">业务页面暂以中文显示,语言偏好将保存在本机。</text>
<button class="language-cancel" @tap="closeLanguage">取消</button>
</view>
</uni-popup>
<uni-popup ref="logoutPopup" type="center" :is-mask-click="true" @change="handleLogoutChange"><view class="logout-dialog-card" role="dialog" aria-modal="true" aria-label="确认退出登录"><PopupHeader title="确认退出登录?" tone="danger" @close="closeLogout" /><text class="logout-dialog-content">退出会撤销当前登录会话并清空本地作业草稿;未提交作业数据不得跨账号保留。</text><view class="logout-dialog-actions"><button class="logout-dialog-cancel" @tap="closeLogout">取消</button><button class="logout-dialog-confirm" @tap="logout">确认退出</button></view></view></uni-popup>
<view v-if="toast" class="app-toast warning" role="status">设置功能将在后续版本开放</view>
</view>
</template>
<script>
import PortalHeader from '@/components/PortalHeader.vue'
import PopupHeader from '@/components/PopupHeader.vue'
import appManifest from '@/manifest.json'
import { getCurrentWarehouseProfile } from '@/api/inbound.js'
import { currentLocale, LOCALE_OPTIONS, setLocale } from '@/i18n/index.js'
import { applyWarehouseProfile, session, requireSession, getSessionRevision } from '@/stores/session.js'
import { logoutCurrentSession } from '@/api/logout-session.js'
import { checkForAppUpdate } from '@/utils/app-update.js'
export default {
components: { PopupHeader, PortalHeader },
data() { return { user: session.currentUser, appVersion: appManifest.versionName, checkingUpdate: false, locale: currentLocale(), languageOptions: LOCALE_OPTIONS, languageOpen: false, logoutOpen: false, toast: false } },
computed: {
displayName() { const name = String(this.user?.name || ''); return name && !/^[a-z0-9._-]+$/i.test(name) ? name : '仓库作业员' },
avatar() { return this.displayName.slice(0, 1).toUpperCase() },
roleLabel() { return '海外仓作业员' },
warehouseDisplay() { return String(this.user?.warehouseName || '').trim() || '海外仓未关联' },
languageLabel() { return LOCALE_OPTIONS.find((item) => item.value === this.locale)?.label || 'English' }
},
onShow() { if (!requireSession()) return; this.user = session.currentUser; this.locale = currentLocale(); this.loadAppVersion(); this.loadWarehouseProfile() },
beforeUnmount() { clearTimeout(this._toastTimer) },
onBackPress() { if (this.languageOpen) { this.closeLanguage(); return true }; if (!this.logoutOpen) return false; this.closeLogout(); return true },
methods: {
loadAppVersion() {
this.appVersion = appManifest.versionName
// #ifdef APP-PLUS
if (typeof plus === 'undefined' || !plus.runtime) return
this.appVersion = String(plus.runtime.version || this.appVersion)
plus.runtime.getProperty(plus.runtime.appid, (properties) => {
this.appVersion = String(properties?.version || plus.runtime.version || appManifest.versionName)
})
// #endif
},
async checkUpdate() {
if (this.checkingUpdate) return
this.checkingUpdate = true
try {
// #ifdef APP-PLUS
await checkForAppUpdate({ manual: true })
return
// #endif
// #ifndef APP-PLUS
uni.showToast({ title: '请在 PDA App 中检查更新', icon: 'none' })
// #endif
} finally { this.checkingUpdate = false }
},
selectLanguage() {
this.languageOpen = true
this.$refs.languagePopup.open()
},
closeLanguage() { this.languageOpen = false; this.$refs.languagePopup.close() },
handleLanguageChange(event) { this.languageOpen = Boolean(event.show) },
applyLanguage(value) {
const selected = LOCALE_OPTIONS.find((item) => item.value === value)
if (!selected) return
if (selected.value !== this.locale) {
this.locale = setLocale(selected.value)
uni.showToast({ title: `语言偏好已保存:${selected.label}`, icon: 'none' })
}
this.closeLanguage()
},
async loadWarehouseProfile() { try { this.user = applyWarehouseProfile(await getCurrentWarehouseProfile()) } catch (_) { this.user = session.currentUser } },
openLogout() { this.logoutOpen = true; this.$refs.logoutPopup.open() }, closeLogout() { this.logoutOpen = false; this.$refs.logoutPopup.close() }, handleLogoutChange(event) { this.logoutOpen = Boolean(event.show) },
showSettings() { clearTimeout(this._toastTimer); this.toast = true; this._toastTimer = setTimeout(() => { this.toast = false }, 2200) },
logout() {
if (this._logoutPromise) return this._logoutPromise
this.closeLogout()
const operation = logoutCurrentSession()
this.user = null
uni.reLaunch({ url: '/pages/login/login' })
this._logoutPromise = operation.catch(error => {
if (getSessionRevision() === error.logoutRevision) {
uni.showToast({ title: '已清除本地登录,服务端退出未确认', icon: 'none' })
}
})
return this._logoutPromise
}
}
}
</script>
<style scoped>
.profile-row-end{flex-shrink:0}
.profile-row-end .profile-value{max-width:none;flex-shrink:0;white-space:nowrap;overflow-wrap:normal}
.language-sheet{box-sizing:border-box;width:100%;max-width:480px;margin:0 auto;padding:4px 16px calc(16px + env(safe-area-inset-bottom,0px));border-radius:20px 20px 0 0;background:#fff}
.language-options{display:flex;flex-direction:column;gap:8px;margin-top:8px}
.language-option{display:flex;box-sizing:border-box;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:52px;margin:0;padding:12px 14px;border:1px solid #dce5f0;border-radius:10px;color:#1f2937;font-size:15px;line-height:22px;text-align:left;background:#fff}
.language-option::after,.language-cancel::after{border:0}
.language-option.selected{border-color:#b8d1f5;color:#135ee8;background:#f1f6ff}
.language-option-name{min-width:0;white-space:nowrap}
.language-option-current{display:flex;flex-shrink:0;align-items:center;gap:6px;color:#135ee8;font-size:12px;line-height:20px}
.language-sheet-hint{display:block;margin:12px 2px;color:#718095;font-size:11px;line-height:18px}
.language-cancel{display:flex;align-items:center;justify-content:center;width:100%;min-height:44px;margin:0;padding:0 12px;border:1px solid #dce5f0;border-radius:10px;color:#526174;font-size:14px;line-height:22px;background:#f7f9fc}
</style>