fix:修复bug

This commit is contained in:
2026-02-06 18:09:23 +08:00
parent f476cee76d
commit bb5a6dd100
51 changed files with 4491 additions and 2630 deletions
+413
View File
@@ -0,0 +1,413 @@
<template>
<view class="profile-page">
<view class="avatar-section">
<view class="avatar-container">
<image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill"></image>
<image v-else class="avatar" src="@/static/head.png" mode="aspectFill"></image>
<!-- 覆盖在头像上的微信选择头像授权按钮仅小程序生效 -->
<!-- #ifdef MP-WEIXIN -->
<button class="avatar-choose-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar"></button>
<!-- #endif -->
</view>
<view class="avatar-tip">{{ $t('userProfile.clickToChange') }}</view>
</view>
<view class="form-section">
<!-- 昵称编辑区域 -->
<view class="form-item nickname-item" :class="{ editing: isEditingNickname }">
<view class="label">{{ $t('userProfile.nickname') }}</view>
<view class="value" v-if="!isEditingNickname" @click="startEditNickname">
<text class="value-text">{{ userInfo.nickName || $t('userProfile.notSet') }}</text>
<uv-icon name="edit-pen" size="16" color="#999999"></uv-icon>
</view>
</view>
<!-- 昵称编辑输入框展开状态 -->
<view class="nickname-edit-area" v-if="isEditingNickname">
<input
class="nickname-input"
v-model="newNickname"
:placeholder="$t('userProfile.enterNickname')"
maxlength="20"
:focus="true"
/>
<view class="edit-buttons">
<button class="cancel-btn" @click="cancelEditNickname">{{ $t('common.cancel') }}</button>
<button class="save-btn" @click="saveNickname">{{ $t('common.save') }}</button>
</view>
</view>
<view class="form-item">
<view class="label">{{ $t('userProfile.phone') }}</view>
<view class="value">
<text class="value-text">{{ userInfo.phone ? maskPhone(userInfo.phone) : $t('userProfile.notBound') }}</text>
</view>
</view>
<!-- <view class="form-item" v-if="userInfo.balanceAmount !== undefined">
<view class="label">{{ $t('userProfile.balance') }}</view>
<view class="value">
<text class="value-text amount">¥{{ userInfo.balanceAmount || '0.00' }}</text>
</view>
</view> -->
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { getMyIndexInfo, uploadUserAvatar, updateUserInfo } from '@/config/api/user.js';
import { useI18n } from '@/utils/i18n.js'
const { t } = useI18n()
// 响应式状态
const userInfo = ref({
nickName: '',
phone: '',
avatar: '',
balanceAmount: '0.00'
});
const newNickname = ref('');
const isEditingNickname = ref(false);
// 页面加载时初始化
onMounted(() => {
uni.setNavigationBarTitle({
title: t('userProfile.title')
})
loadUserInfo();
});
// 获取用户信息
const loadUserInfo = async () => {
try {
const res = await getMyIndexInfo();
console.log('User info response:', res);
if (res.code == 401 || res.code == 40101) {
redirectToLogin();
return;
} else if (res.code == 200) {
userInfo.value = {
nickName: res.data.nickname,
phone: res.data.phone,
avatar: res.data.iconUrl,
balanceAmount: res.data.balanceAmount || '0.00',
isAdmin: res.data.isAdmin
};
uni.setStorageSync('userInfo', userInfo.value);
}
} catch (error) {
console.error('获取用户信息失败:', error);
uni.showToast({
title: t('user.getUserInfoFailed'),
icon: 'none'
});
}
};
// 跳转登录
const redirectToLogin = () => {
try {
const pages = getCurrentPages();
const current = pages && pages.length ? pages[pages.length - 1] : null;
const route = current && current.route ? ('/' + current.route) : '/pages/index/index';
const query = current && current.options ? Object.keys(current.options).map(k =>
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : '';
const redirect = encodeURIComponent(query ? `${route}?${query}` : route);
uni.reLaunch({
url: `/pages/login/index?redirect=${redirect}`
});
} catch (e) {
uni.reLaunch({
url: '/pages/login/index'
});
}
};
// 小程序原生选择头像回调
const onChooseAvatar = async (e) => {
try {
const token = uni.getStorageSync('token');
if (!token) {
redirectToLogin();
return;
}
const avatarLocalPath = e?.detail?.avatarUrl;
if (!avatarLocalPath) {
uni.showToast({
title: t('user.noAvatar'),
icon: 'none'
});
return;
}
uni.showLoading({
title: t('userProfile.uploading'),
mask: true
});
const uploadRes = await uploadUserAvatar(avatarLocalPath);
const serverAvatar = uploadRes?.data?.url || uploadRes?.url || uploadRes?.data || '';
if (serverAvatar) {
userInfo.value = {
...userInfo.value,
avatar: serverAvatar
};
uni.setStorageSync('userInfo', userInfo.value);
}
uni.showToast({
title: t('user.avatarUpdated'),
icon: 'success'
});
await loadUserInfo();
} catch (err) {
console.error('选择/上传头像失败:', err);
uni.showToast({
title: t('user.avatarUploadFailed'),
icon: 'none'
});
} finally {
uni.hideLoading();
}
};
// 开始编辑昵称
const startEditNickname = () => {
newNickname.value = userInfo.value.nickName || '';
isEditingNickname.value = true;
};
// 取消编辑昵称
const cancelEditNickname = () => {
isEditingNickname.value = false;
newNickname.value = '';
};
// 保存昵称
const saveNickname = async () => {
if (!newNickname.value || !newNickname.value.trim()) {
uni.showToast({
title: t('userProfile.nicknameRequired'),
icon: 'none'
});
return;
}
try {
uni.showLoading({
title: t('userProfile.saving'),
mask: true
});
// 先获取最新的用户信息,确保数据是最新的
const latestUserInfo = await getMyIndexInfo();
if (latestUserInfo.code !== 200) {
throw new Error('获取用户信息失败');
}
// 使用最新的服务器数据,只修改昵称字段
const updateData = {
nickname: newNickname.value.trim(),
phone: latestUserInfo.data.phone,
iconUrl: latestUserInfo.data.iconUrl,
// 保留其他可能的字段
...latestUserInfo.data
};
// 确保昵称使用新值
updateData.nickname = newNickname.value.trim();
// 调用后端接口更新用户信息
const res = await updateUserInfo(updateData);
if (res.code === 200) {
// 更新成功后重新获取用户信息,确保数据同步
await loadUserInfo();
uni.hideLoading();
uni.showToast({
title: t('userProfile.nicknameUpdated'),
icon: 'success'
});
isEditingNickname.value = false;
} else {
throw new Error(res.message || t('userProfile.updateFailed'));
}
} catch (error) {
console.error('修改昵称失败:', error);
uni.hideLoading();
uni.showToast({
title: error.message || t('userProfile.updateFailed'),
icon: 'none'
});
}
};
// 手机号掩码函数
function maskPhone(phone) {
if (!phone) return '';
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}
</script>
<style lang="scss" scoped>
.profile-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: env(safe-area-inset-bottom);
}
.avatar-section {
background: linear-gradient(180deg, #D1FFE1 0%, #ffffff 100%);
padding: 60rpx 0 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.avatar-container {
position: relative;
margin-bottom: 20rpx;
}
.avatar {
width: 160rpx;
height: 160rpx;
border-radius: 80rpx;
background-color: #f0f0f0;
display: block;
}
/* 仅小程序端存在,此按钮覆盖在头像上捕获点击以触发选择头像 */
/* #ifdef MP-WEIXIN */
.avatar-choose-btn {
position: absolute;
left: 0;
top: 0;
width: 160rpx;
height: 160rpx;
border: none;
background: transparent;
padding: 0;
margin: 0;
opacity: 0;
border-radius: 80rpx;
}
/* #endif */
.avatar-tip {
font-size: 24rpx;
color: #999999;
}
.form-section {
margin: 20rpx 30rpx;
background-color: #ffffff;
border-radius: 20rpx;
overflow: hidden;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
transition: all 0.3s ease;
}
.form-item:last-child {
border-bottom: none;
}
.form-item.nickname-item.editing {
border-bottom: none;
padding-bottom: 20rpx;
}
.label {
font-size: 30rpx;
color: #333333;
font-weight: 500;
}
.value {
display: flex;
align-items: center;
}
.value-text {
font-size: 28rpx;
color: #666666;
margin-right: 10rpx;
}
.value-text.amount {
color: #e2231a;
font-weight: 600;
font-size: 32rpx;
}
/* 昵称编辑区域样式 */
.nickname-edit-area {
padding: 0 30rpx 30rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
animation: slideDown 0.3s ease;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.nickname-input {
width: 100%;
height: 80rpx;
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
box-sizing: border-box;
margin-bottom: 20rpx;
background-color: #fafafa;
}
.edit-buttons {
display: flex;
justify-content: flex-end;
gap: 20rpx;
}
.cancel-btn,
.save-btn {
padding: 0 40rpx;
height: 64rpx;
line-height: 64rpx;
text-align: center;
border-radius: 32rpx;
font-size: 28rpx;
border: none;
min-width: 120rpx;
}
.cancel-btn {
background-color: #f5f5f5;
color: #666666;
}
.save-btn {
background: linear-gradient(135deg, #42d392 0%, #28c76f 100%);
color: #ffffff;
}
</style>