appointment_system/pages/me/profile-edit-page.vue
2025-12-11 22:50:18 +08:00

437 lines
8.7 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="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>