437 lines
8.7 KiB
Vue
437 lines
8.7 KiB
Vue
<template>
|
||
<view class="container">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="header">
|
||
<!-- 状态栏占位 -->
|
||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||
<view class="header-content">
|
||
<!-- 返回按钮 -->
|
||
<view class="back-button" @click="goBack">
|
||
<text class="back-icon">‹</text>
|
||
</view>
|
||
<!-- 页面标题 -->
|
||
<text class="header-title">{{ $t('profile.title') || '个人资料' }}</text>
|
||
<!-- 右侧占位,保持标题居中 -->
|
||
<view class="header-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 主要内容区域 -->
|
||
<view class="content">
|
||
<!-- 头像区域 -->
|
||
<view class="avatar-section">
|
||
<view class="avatar-wrapper">
|
||
<!-- 用户头像,点击可更换 -->
|
||
<image :src="form.avatar || '/static/default-avatar.png'" class="avatar" mode="aspectFill"
|
||
@click="chooseAvatar">
|
||
</image>
|
||
<!-- 编辑图标 -->
|
||
<view class="avatar-edit-icon">
|
||
<text class="edit-icon">✏️</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 表单区域 -->
|
||
<view class="form-section">
|
||
<!-- 昵称输入 -->
|
||
<view class="form-item">
|
||
<text class="form-label">{{ $t('profile.nickname') || '昵称' }}</text>
|
||
<input v-model="form.nickname" class="form-input"
|
||
:placeholder="$t('profile.nicknamePlaceholder') || '请输入昵称'" maxlength="20" />
|
||
</view>
|
||
|
||
<!-- 用户UID(只读) -->
|
||
<view class="form-item readonly">
|
||
<text class="form-label">UID</text>
|
||
<text class="form-value">{{ userInfo.uid || '-' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 保存按钮区域 -->
|
||
<view class="button-section">
|
||
<button class="save-button" @click="handleSave" :loading="saving" :disabled="saving">
|
||
{{ $t('common.save') || '保存' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
AppServer
|
||
} from '@/modules/api/AppServer.js'
|
||
import {
|
||
getUserInfo,
|
||
saveUserInfo
|
||
} from '@/utils/auth.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
statusBarHeight: 0, // 状态栏高度
|
||
userInfo: {}, // 用户信息
|
||
form: {
|
||
avatar: '', // 头像URL
|
||
nickname: '' // 昵称
|
||
},
|
||
saving: false // 保存中状态
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 页面加载时执行
|
||
*/
|
||
onLoad() {
|
||
// 获取状态栏高度,用于适配不同机型
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
this.statusBarHeight = systemInfo.statusBarHeight || 0
|
||
|
||
// 加载用户信息
|
||
this.loadUserInfo()
|
||
},
|
||
|
||
methods: {
|
||
/**
|
||
* 加载用户信息
|
||
* 先从本地缓存读取,再从服务器获取最新数据
|
||
*/
|
||
loadUserInfo() {
|
||
// 先从本地缓存获取
|
||
const localUserInfo = getUserInfo()
|
||
if (localUserInfo) {
|
||
this.userInfo = localUserInfo
|
||
this.form = {
|
||
avatar: localUserInfo.avatar || '',
|
||
nickname: localUserInfo.nickname || ''
|
||
}
|
||
}
|
||
|
||
// 从服务器获取最新数据
|
||
this.fetchUserProfile()
|
||
},
|
||
|
||
/**
|
||
* 从服务器获取用户信息
|
||
*/
|
||
async fetchUserProfile() {
|
||
try {
|
||
const appserver = new AppServer()
|
||
const response = await appserver.GetUserProfile()
|
||
|
||
if (response.code === 0 && response.data) {
|
||
this.userInfo = response.data
|
||
this.form = {
|
||
avatar: response.data.avatar || '',
|
||
nickname: response.data.nickname || ''
|
||
}
|
||
// 更新本地缓存
|
||
saveUserInfo(response.data)
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户信息失败:', error)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 选择头像
|
||
* 调用系统相册或相机选择图片
|
||
*/
|
||
chooseAvatar() {
|
||
uni.chooseImage({
|
||
count: 1, // 只选择一张
|
||
sizeType: ['compressed'], // 压缩图片
|
||
sourceType: ['album', 'camera'], // 相册和相机都可以
|
||
success: (res) => {
|
||
const tempFilePath = res.tempFilePaths[0]
|
||
this.uploadAvatar(tempFilePath)
|
||
}
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 上传头像到服务器
|
||
* @param {String} filePath - 本地图片路径
|
||
*/
|
||
async uploadAvatar(filePath) {
|
||
uni.showLoading({
|
||
title: '上传中...',
|
||
mask: true
|
||
})
|
||
|
||
try {
|
||
const appserver = new AppServer()
|
||
const response = await appserver.UploadImage(filePath)
|
||
|
||
if (response.code === 0 && response.data) {
|
||
// 上传成功,更新头像URL
|
||
this.form.avatar = response.data.url
|
||
uni.showToast({
|
||
title: '头像上传成功',
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
throw new Error(response.message || '上传失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('上传头像失败:', error)
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 保存用户信息
|
||
*/
|
||
async handleSave() {
|
||
// 验证昵称不能为空
|
||
if (!this.form.nickname || !this.form.nickname.trim()) {
|
||
uni.showToast({
|
||
title: '请输入昵称',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
this.saving = true
|
||
|
||
try {
|
||
const appserver = new AppServer()
|
||
const updateData = {
|
||
nickname: this.form.nickname.trim(),
|
||
avatar: this.form.avatar
|
||
}
|
||
|
||
const response = await appserver.UpdateUserProfile(updateData)
|
||
|
||
if (response.code === 0) {
|
||
// 更新本地缓存
|
||
const updatedUserInfo = {
|
||
...this.userInfo,
|
||
...updateData
|
||
}
|
||
saveUserInfo(updatedUserInfo)
|
||
|
||
uni.showToast({
|
||
title: '保存成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 延迟返回上一页
|
||
setTimeout(() => {
|
||
this.goBack()
|
||
}, 1000)
|
||
} else {
|
||
throw new Error(response.message || '保存失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('保存失败:', error)
|
||
uni.showToast({
|
||
title: error.message || '保存失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.saving = false
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 返回上一页
|
||
*/
|
||
goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 页面容器 */
|
||
.container {
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
/* 顶部导航栏 */
|
||
.header {
|
||
background-color: #fff;
|
||
border-bottom: 1px solid #eee;
|
||
|
||
/* 状态栏占位 */
|
||
.status-bar {
|
||
width: 100%;
|
||
background-color: #fff;
|
||
}
|
||
|
||
/* 导航栏内容 */
|
||
.header-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 16px;
|
||
}
|
||
|
||
/* 返回按钮 */
|
||
.back-button {
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 返回图标 */
|
||
.back-icon {
|
||
font-size: 44px;
|
||
color: #333;
|
||
font-weight: 300;
|
||
}
|
||
|
||
/* 页面标题 */
|
||
.header-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 右侧占位 */
|
||
.header-placeholder {
|
||
width: 40px;
|
||
}
|
||
}
|
||
|
||
/* 主要内容区域 */
|
||
.content {
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
/* 头像区域 */
|
||
.avatar-section {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 60rpx 0;
|
||
background-color: #fff;
|
||
margin-bottom: 20rpx;
|
||
|
||
/* 头像容器 */
|
||
.avatar-wrapper {
|
||
position: relative;
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
}
|
||
|
||
/* 头像图片 */
|
||
.avatar {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
background-color: #e0e0e0;
|
||
}
|
||
|
||
/* 编辑图标 */
|
||
.avatar-edit-icon {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
background-color: #17a2b8;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 3px solid #fff;
|
||
|
||
.edit-icon {
|
||
font-size: 20rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 表单区域 */
|
||
.form-section {
|
||
background-color: #fff;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
/* 表单项 */
|
||
.form-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 28rpx 32rpx;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
/* 只读样式 */
|
||
&.readonly {
|
||
.form-value {
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
/* 表单标签 */
|
||
.form-label {
|
||
width: 160rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 表单输入框 */
|
||
.form-input {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
text-align: right;
|
||
}
|
||
|
||
/* 表单值(只读) */
|
||
.form-value {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
/* 按钮区域 */
|
||
.button-section {
|
||
padding: 40rpx 32rpx;
|
||
}
|
||
|
||
/* 保存按钮 */
|
||
.save-button {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
|
||
color: #fff;
|
||
border-radius: 44rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
border: none;
|
||
box-shadow: 0 4px 12px rgba(23, 162, 184, 0.3);
|
||
}
|
||
|
||
/* 移除按钮默认边框 */
|
||
.save-button::after {
|
||
border: none;
|
||
}
|
||
|
||
/* 按钮禁用状态 */
|
||
.save-button[disabled] {
|
||
opacity: 0.6;
|
||
}
|
||
</style>
|