<template>
<view class="user-center">
<!-- 头部导航 -->
<view class="header">
<text class="title">个人中心</text>
</view>
<!-- 个人信息卡片 -->
<view class="info-card">
<!-- 头像区域 -->
<view class="info-item" @click="editAvatar">
<text class="label">头像</text>
<view class="content">
<image :src="userInfo.avatar" class="avatar" mode="aspectFill" />
<text class="arrow">></text>
</view>
</view>
<!-- 昵称区域 -->
<view class="info-item" @click="editNickname">
<text class="label">昵称</text>
<view class="content">
<text>{{ userInfo.nickname || '未设置' }}</text>
<text class="arrow">></text>
</view>
</view>
<!-- 性别区域 -->
<view class="info-item" @click="editGender">
<text class="label">性别</text>
<view class="content">
<text>{{ userInfo.gender || '未设置' }}</text>
<text class="arrow">></text>
</view>
</view>
<!-- 年龄区域 -->
<view class="info-item" @click="editAge">
<text class="label">年龄</text>
<view class="content">
<text>{{ userInfo.age || '未设置' }}</text>
<text class="arrow">></text>
</view>
</view>
</view>
<!-- 退出登录按钮 -->
<view class="logout-btn" @click="logout">
退出登录
</view>
<!-- 编辑弹窗 -->
<view class="modal" v-if="showModal">
<view class="modal-content">
<view class="modal-header">
<text class="modal-title">{{ modalTitle }}</text>
<text class="close" @click="closeModal">×</text>
</view>
<view class="modal-body">
<view v-if="modalType === 'avatar'">
<text>选择头像来源</text>
<view class="avatar-options">
<view class="option" @click="chooseAvatar('camera')">
<image src="/static/camera.jpg" class="option-icon" mode="aspectFill" />
<text>拍照</text>
</view>
<view class="option" @click="chooseAvatar('album')">
<image src="/static/album.jpg" class="option-icon" mode="aspectFill" />
<text>从相册选择</text>
</view>
</view>
</view>
<view v-else-if="modalType === 'nickname'">
<input
type="text"
class="input"
placeholder="请输入昵称"
v-model="editValue"
/>
</view>
<view v-else-if="modalType === 'gender'">
<view class="gender-options">
<view class="option" :class="{ active: editValue === '男' }" @click="editValue = '男'">
男
</view>
<view class="option" :class="{ active: editValue === '女' }" @click="editValue = '女'">
女
</view>
<view class="option" :class="{ active: editValue === '保密' }" @click="editValue = '保密'">
保密
</view>
</view>
</view>
<view v-else-if="modalType === 'age'">
<input
type="number"
class="input"
placeholder="请输入年龄"
v-model="editValue"
/>
</view>
</view>
<view class="modal-footer">
<button class="cancel-btn" @click="closeModal">取消</button>
<button class="confirm-btn" @click="confirmEdit">确定</button>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
userInfo: {
avatar: '/static/user.jpg',
nickname: uni.getStorageSync('currentUser') || '',
gender: '',
age: ''
},
showModal: false,
modalType: '',
modalTitle: '',
editValue: ''
}
},
onShow() {
this.loadUserInfo();
},
methods: {
loadUserInfo() {
const savedInfo = uni.getStorageSync('userInfo') || {};
this.userInfo = { ...this.userInfo, ...savedInfo };
console.log('加载的用户信息:', this.userInfo);
},
editAvatar() {
this.modalType = 'avatar';
this.modalTitle = '更换头像';
this.showModal = true;
},
// 优化后的chooseAvatar方法(核心修改)
async chooseAvatar(type) {
// 显示加载提示,提升用户体验
uni.showLoading({ title: '处理中...' });
try {
// 1. 选择图片(Promise封装,便于async/await)
const chooseRes = await new Promise((resolve, reject) => {
uni.chooseImage({
count: 1,
sourceType: type === 'camera' ? ['camera'] : ['album'],
sizeType: ['compressed'], // 优先压缩,减少文件大小
success: resolve,
fail: (err) => reject(new Error(`选择图片失败: ${err.errMsg}`))
});
});
// 2. 准备文件系统和路径
const fs = wx.getFileSystemManager();
const timestamp = Date.now(); // 时间戳确保文件名唯一
const fileName = `avatar_${timestamp}.png`;
const savedPath = `${wx.env.USER_DATA_PATH}/${fileName}`; // 固定存储目录
// 3. 保存文件为永久路径
await new Promise((resolve, reject) => {
fs.saveFile({
tempFilePath: chooseRes.tempFilePaths[0],
filePath: savedPath,
success: (res) => {
console.log('[user-center] 图片保存成功,路径:', res.savedFilePath);
resolve(res.savedFilePath);
},
fail: (err) => reject(new Error(`保存图片失败: ${err.errMsg}`))
});
});
// 4. 更新用户信息并同步存储
const userInfo = { ...uni.getStorageSync('userInfo') || {} }; // 深拷贝避免引用问题
userInfo.avatar = savedPath;
uni.setStorageSync('userInfo', userInfo);
console.log('[user-center] 本地存储已更新:', userInfo.avatar);
// 5. 实时更新视图并通知其他组件
this.userInfo.avatar = savedPath; // 更新当前组件
uni.$emit('avatarUpdated', savedPath); // 通知my-title组件
console.log('[user-center] 已发送全局更新事件');
// 6. 关闭弹窗和加载提示
this.closeModal();
uni.hideLoading();
uni.showToast({ title: '头像更新成功', icon: 'success' });
} catch (err) {
// 统一错误处理
console.error('[user-center] 头像处理异常:', err);
uni.hideLoading(); // 确保加载提示关闭
uni.showToast({
title: err.message || '更新失败,请重试',
icon: 'none',
duration: 2000
});
}
},
editNickname() {
this.modalType = 'nickname';
this.modalTitle = '修改昵称';
this.editValue = this.userInfo.nickname || '';
this.showModal = true;
},
editGender() {
this.modalType = 'gender';
this.modalTitle = '修改性别';
this.editValue = this.userInfo.gender || '保密';
this.showModal = true;
},
editAge() {
this.modalType = 'age';
this.modalTitle = '修改年龄';
this.editValue = this.userInfo.age || '';
this.showModal = true;
},
confirmEdit() {
if (this.modalType === 'nickname') {
this.userInfo.nickname = this.editValue;
} else if (this.modalType === 'gender') {
this.userInfo.gender = this.editValue;
} else if (this.modalType === 'age') {
this.userInfo.age = this.editValue;
}
this.saveUserInfo();
this.closeModal();
uni.showToast({ title: '修改成功', icon: 'success' });
},
closeModal() {
this.showModal = false;
this.editValue = '';
},
saveUserInfo() {
uni.setStorageSync('userInfo', this.userInfo);
},
logout() {
uni.showModal({
title: '确认退出',
content: '确定要退出当前账号吗?',
success: (res) => {
if (res.confirm) {
uni.removeStorageSync('isLoggedIn');
uni.removeStorageSync('currentUser');
uni.removeStorageSync('userInfo');
uni.navigateTo({ url: '/pages/login/login' });
}
}
});
}
}
}
</script>
<style lang="scss">
.user-center {
background-color: #f5f5f5;
min-height: 100vh;
.header {
height: 100rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1rpx solid #eee;
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
}
.info-card {
background-color: #fff;
margin-top: 20rpx;
padding: 0 30rpx;
.info-item {
display: flex;
align-items: center;
height: 100rpx;
border-bottom: 1rpx solid #eee;
.label {
font-size: 32rpx;
color: #333;
width: 140rpx;
}
.content {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 20rpx;
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
text {
font-size: 30rpx;
color: #666;
}
}
.arrow {
font-size: 30rpx;
color: #999;
margin-left: 10rpx;
}
}
}
.logout-btn {
margin: 60rpx 30rpx;
height: 90rpx;
background-color: #ff4d4f;
color: #fff;
font-size: 34rpx;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
.modal-content {
background-color: #fff;
width: 600rpx;
border-radius: 20rpx;
.modal-header {
height: 100rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
border-bottom: 1rpx solid #eee;
.modal-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.close {
font-size: 40rpx;
color: #999;
}
}
.modal-body {
padding: 30rpx;
.avatar-options {
display: flex;
justify-content: space-around;
margin-top: 40rpx;
.option {
display: flex;
flex-direction: column;
align-items: center;
.option-icon {
width: 120rpx;
height: 120rpx;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
}
}
.gender-options {
display: flex;
justify-content: space-around;
margin-top: 20rpx;
.option {
width: 160rpx;
height: 80rpx;
border: 1rpx solid #ddd;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
&.active {
border-color: #007aff;
color: #007aff;
}
}
}
.input {
width: 100%;
height: 80rpx;
border: 1rpx solid #ddd;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 32rpx;
}
}
.modal-footer {
display: flex;
height: 100rpx;
border-top: 1rpx solid #eee;
.cancel-btn,
.confirm-btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 34rpx;
}
.confirm-btn {
color: #007aff;
font-weight: bold;
}
}
}
}
}
</style>
<template>
<view class="my-title">
<!-- Logo -->
<navigator class="logo" url="/pages/index/index">
<image class="logo-img" src="/static/logo.png" mode="aspectFill" />
</navigator>
<!-- 搜索框 -->
<view class="search-icon">
<image src="/static/search.jpg" mode="aspectFill" />
</view>
<!-- 用户头像 -->
<view class="user-icon">
<image
:src="userAvatar"
mode="aspectFill"
@error="handleImgError"
@load="handleImgLoad"
:key="avatarKey"
/>
</view>
<!-- 下载APP按钮 -->
<view class="down-app">
下载APP
</view>
</view>
</template>
<script>
export default {
name: "MyTitle",
data() {
return {
userAvatar: '/static/user.jpg',
avatarKey: 0,
syncTimer: null
};
},
// 页面显示时强制刷新(解决切页后不同步问题)
onShow() {
console.log('[my-title] 页面显示,强制加载最新头像');
this.loadAvatar();
},
onLoad() {
console.log('[my-title] 初始化,开始监听事件');
// 监听全局事件(确保任何页面修改都能触发)
uni.$on('avatarUpdated', (newPath) => {
console.log('[my-title] 全局事件触发,新路径:', newPath);
this.updateAvatar(newPath);
});
// 定时同步(3秒一次,平衡性能和可靠性)
this.syncTimer = setInterval(() => {
console.log('[my-title] 定时同步触发');
this.loadAvatar();
}, 3000);
},
onUnload() {
console.log('[my-title] 页面卸载,清理资源');
uni.$off('avatarUpdated');
clearInterval(this.syncTimer);
},
methods: {
// 更新头像(极简逻辑,减少拦截)
updateAvatar(newPath) {
if (!newPath) {
console.error('[my-title] 路径为空,跳过更新');
return;
}
// 直接更新,不做过度验证(信任用户中心传递的路径)
this.userAvatar = newPath;
this.avatarKey++; // 强制重新渲染
console.log('[my-title] 头像已更新为:', newPath);
},
// 从存储加载(核心:确保读取最新数据)
loadAvatar() {
try {
const userInfo = uni.getStorageSync('userInfo') || {};
const storedPath = userInfo.avatar || '/static/user.jpg';
console.log('[my-title] 从存储读取到路径:', storedPath);
// 只要路径不同就更新
if (storedPath !== this.userAvatar) {
this.updateAvatar(storedPath);
}
} catch (e) {
console.error('[my-title] 读取存储失败:', e);
}
},
// 加载成功回调
handleImgLoad() {
console.log('[my-title] 头像加载成功,当前路径:', this.userAvatar);
},
// 加载失败处理(增强重试)
handleImgError(e) {
console.error('[my-title] 头像加载失败,错误:', e.errMsg);
console.error('[my-title] 失败路径:', this.userAvatar);
// 重试存储中的最新路径
setTimeout(() => {
console.log('[my-title] 失败后重试加载...');
this.loadAvatar();
}, 1000);
}
}
}
</script>
<style scoped>
/* 样式保持不变 */
.my-title{
display: flex;
align-items: center;
padding: 10rpx 20rpx;
background-color: #fff;
height: 100rpx;
}
.logo {
flex: 2;
display: flex;
align-items: center;
}
.logo-img {
width: 180rpx;
height: 60rpx;
}
.search-icon {
flex: 3;
display: flex;
justify-content: center;
align-items: center;
}
.search-icon image {
width: 60rpx;
height: 44rpx;
}
.user-icon {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.user-icon image {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: 1rpx solid #eee;
}
.down-app {
flex: 2;
font-size: 30rpx;
background-color: #87CEEB;
color: #fff;
border-radius: 10rpx;
padding: 10rpx 20rpx;
margin-left: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
</style>如何修改头像后用户头像也会发生改变
最新发布