This commit is contained in:
sjk
2025-11-17 14:11:46 +08:00
commit ad4a600af9
1659 changed files with 171560 additions and 0 deletions

View File

@@ -0,0 +1,540 @@
import { fetchPerson } from '../../../services/usercenter/fetchPerson';
import { phoneEncryption } from '../../../utils/util';
import Toast from 'tdesign-miniprogram/toast/index';
const weChatAuthService = require('../../../services/auth/wechat');
Page({
data: {
personInfo: {
avatarUrl: '',
nickName: '',
gender: 0, // 默认为0未设置
phoneNumber: '',
level: 1
},
originalPersonInfo: null, // 保存原始数据
displayPhoneNumber: '', // 用于显示的加密手机号
defaultAvatarUrl: 'https://tdesign.gtimg.com/miniprogram/template/retail/usercenter/icon-user-center-avatar@2x.png',
genderVisible: false,
genderList: [
{ label: '男', value: 1 },
{ label: '女', value: 2 },
{ label: '未设置', value: 0 }
],
// 性别映射
genderMap: {
0: '未设置',
1: '男',
2: '女'
},
saving: false
},
onLoad() {
this.init();
},
init() {
this.fetchData();
},
fetchData() {
console.log('=== 开始获取用户信息 ===');
fetchPerson().then((personInfo) => {
console.log('从API获取到的原始用户信息:', personInfo);
// 确保所有字段都有正确的类型和默认值
const safePersonInfo = {
avatarUrl: personInfo.avatarUrl || '',
nickName: personInfo.nickName || '',
gender: personInfo.gender || 0,
phoneNumber: personInfo.phoneNumber || '',
level: personInfo.level || 1,
};
console.log('处理后的安全用户信息:', safePersonInfo);
console.log('性别数据详细信息:', {
原始性别: personInfo.gender,
处理后性别: safePersonInfo.gender,
性别类型: typeof safePersonInfo.gender
});
console.log('手机号数据详细信息:', {
原始手机号: personInfo.phoneNumber,
处理后手机号: safePersonInfo.phoneNumber,
手机号类型: typeof safePersonInfo.phoneNumber
});
console.log('个人资料页 - 头像URL:', safePersonInfo.avatarUrl);
console.log('个人资料页 - 默认头像URL:', this.data.defaultAvatarUrl);
// 保存原始数据用于编辑,显示数据用于界面展示
this.setData({
personInfo: safePersonInfo,
originalPersonInfo: JSON.parse(JSON.stringify(safePersonInfo)), // 深拷贝保存原始数据
originalNickname: safePersonInfo.nickName, // 保存原始昵称用于重置
displayPhoneNumber: phoneEncryption(safePersonInfo.phoneNumber), // 仅用于显示的加密手机号
});
console.log('设置到页面数据后的personInfo:', this.data.personInfo);
console.log('原始数据originalPersonInfo:', this.data.originalPersonInfo);
}).catch((error) => {
console.error('获取用户信息失败:', error);
Toast({
context: this,
selector: '#t-toast',
message: '获取用户信息失败',
theme: 'error',
});
});
},
onClickCell({ currentTarget }) {
const { dataset } = currentTarget;
const { nickName } = this.data.personInfo;
switch (dataset.type) {
case 'name':
wx.navigateTo({
url: `/pages/user/name-edit/index?name=${nickName}`,
});
break;
case 'avatarUrl':
this.toModifyAvatar();
break;
default: {
break;
}
}
},
// 保存性别设置
async saveGender(gender) {
this.setData({ saving: true });
try {
const userInfo = {
nickName: this.data.personInfo.nickName,
avatarUrl: this.data.personInfo.avatarUrl,
gender: gender
};
await weChatAuthService.updateUserProfile(userInfo);
this.setData({ saving: false });
Toast({
context: this,
selector: '#t-toast',
message: '性别设置成功',
theme: 'success',
});
} catch (error) {
console.error('保存性别失败:', error);
this.setData({ saving: false });
Toast({
context: this,
selector: '#t-toast',
message: '性别设置失败,请重试',
theme: 'error',
});
}
},
// 处理头像昵称组件的回调(微信官方组件)- 自动保存
async onChooseAvatar(e) {
const { avatarUrl } = e.detail;
console.log('从微信头像组件获取头像:', avatarUrl);
// 立即更新界面显示
this.setData({
'personInfo.avatarUrl': avatarUrl,
});
// 自动保存头像
await this.saveAvatar(avatarUrl);
},
// 保存头像
async saveAvatar(avatarUrl) {
this.setData({ saving: true });
try {
const userInfo = {
nickName: this.data.personInfo.nickName,
avatarUrl: avatarUrl,
gender: this.data.personInfo.gender
};
console.log('保存头像信息:', userInfo);
await weChatAuthService.updateUserProfile(userInfo);
// 确保头像URL已保存到本地数据
this.setData({
saving: false,
'personInfo.avatarUrl': avatarUrl
});
Toast({
context: this,
selector: '#t-toast',
message: '头像设置成功',
theme: 'success',
});
} catch (error) {
console.error('保存头像失败:', error);
this.setData({ saving: false });
Toast({
context: this,
selector: '#t-toast',
message: '头像设置失败,请重试',
theme: 'error',
});
}
},
// 昵称输入变化
onNicknameChange(e) {
console.log('昵称输入变化:', e.detail.value);
this.setData({
'personInfo.nickName': e.detail.value
});
},
// 保存昵称(兼容旧方式)
saveNickname(e) {
console.log('保存昵称事件触发:', e);
const nickname = e.detail.value || this.data.personInfo.nickName;
console.log('要保存的昵称:', nickname);
if (!nickname || nickname.trim() === '') {
wx.showToast({
title: '昵称不能为空',
icon: 'none'
});
return;
}
this.saveNicknameToServer(nickname.trim());
},
// 性别选择事件radio-group change事件
onGenderChange(e) {
const gender = parseInt(e.detail.value);
console.log('性别选择变化:', gender);
this.setData({
'personInfo.gender': gender
});
// 自动保存性别
this.saveGender(gender);
},
// 手机号获得焦点事件
onPhoneFocus(e) {
console.log('手机号输入框获得焦点');
// 确保显示原始手机号而不是加密的
if (this.data.originalPersonInfo && this.data.originalPersonInfo.phoneNumber) {
this.setData({
displayPhoneNumber: this.data.originalPersonInfo.phoneNumber
});
}
},
// 手机号输入事件(实时输入)
onPhoneInput(e) {
const phoneNumber = e.detail.value.trim();
// 更新显示数据和实际数据
this.setData({
displayPhoneNumber: phoneNumber,
'personInfo.phoneNumber': phoneNumber
});
// 清除之前的定时器
if (this.phoneInputTimer) {
clearTimeout(this.phoneInputTimer);
}
// 设置延迟自动保存用户停止输入1.5秒后自动保存)
this.phoneInputTimer = setTimeout(() => {
this.updatePhoneNumber(phoneNumber, 'auto');
}, 1500);
},
// 手机号输入失焦事件
onPhoneBlur(e) {
const phoneNumber = e.detail.value.trim();
// 清除定时器,立即保存
if (this.phoneInputTimer) {
clearTimeout(this.phoneInputTimer);
this.phoneInputTimer = null;
}
this.updatePhoneNumber(phoneNumber, 'blur');
// 失焦后显示加密的手机号
if (phoneNumber) {
this.setData({
displayPhoneNumber: phoneEncryption(phoneNumber)
});
}
},
// 手机号输入确认事件(回车)
onPhoneConfirm(e) {
const phoneNumber = e.detail.value.trim();
// 清除定时器,立即保存
if (this.phoneInputTimer) {
clearTimeout(this.phoneInputTimer);
this.phoneInputTimer = null;
}
this.updatePhoneNumber(phoneNumber, 'confirm');
},
// 更新手机号
updatePhoneNumber(phoneNumber, triggerType = 'manual') {
console.log(`=== 手机号更新触发 (${triggerType}) ===`);
console.log('输入的手机号:', phoneNumber);
// 如果手机号为空,直接保存(清空操作)
if (!phoneNumber) {
console.log('手机号为空,执行清空操作');
this.setData({
'personInfo.phoneNumber': phoneNumber
});
this.savePhoneNumber(phoneNumber, triggerType);
return;
}
// 验证手机号格式
if (!this.validatePhoneNumber(phoneNumber)) {
// 只在非自动触发时显示错误提示
if (triggerType !== 'auto') {
wx.showToast({
title: '手机号格式不正确',
icon: 'none'
});
}
console.log('手机号格式验证失败:', phoneNumber);
return;
}
// 检查是否与原始值相同,避免重复保存
const originalPhoneNumber = this.data.originalPersonInfo ? this.data.originalPersonInfo.phoneNumber : '';
if (phoneNumber === originalPhoneNumber) {
console.log('手机号未变化,跳过保存');
return;
}
// 更新数据
this.setData({
'personInfo.phoneNumber': phoneNumber
});
// 自动保存手机号
this.savePhoneNumber(phoneNumber, triggerType);
},
// 验证手机号格式
validatePhoneNumber(phoneNumber) {
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phoneNumber);
},
// 保存手机号
async savePhoneNumber(phoneNumber, triggerType = 'manual') {
if (this.data.saving) {
console.log('正在保存中,跳过重复请求');
return;
}
this.setData({ saving: true });
try {
console.log(`=== 开始保存手机号到服务器 (${triggerType}) ===`);
console.log('要保存的手机号:', phoneNumber);
// 构建用户信息对象,包含手机号
const userInfo = {
nickName: this.data.personInfo.nickName,
avatarUrl: this.data.personInfo.avatarUrl,
gender: this.data.personInfo.gender,
phoneNumber: phoneNumber // 添加手机号字段
};
console.log('发送到服务器的用户信息:', userInfo);
// 调用微信授权服务更新用户信息
const result = await weChatAuthService.updateUserProfile(userInfo);
console.log('服务器返回结果:', result);
// 根据触发方式显示不同的提示
if (triggerType === 'auto') {
// 自动保存时显示更简洁的提示
console.log('自动保存成功');
} else {
// 手动触发时显示Toast提示
wx.showToast({
title: phoneNumber ? '手机号保存成功' : '手机号已清空',
icon: 'success',
duration: 1500
});
}
// 保存成功后,立即更新原始数据
if (this.data.originalPersonInfo) {
this.setData({
'originalPersonInfo.phoneNumber': phoneNumber
});
}
// 重新获取用户信息验证是否真的更新了
console.log('=== 重新获取用户信息验证更新 ===');
this.fetchData();
} catch (error) {
console.error('保存手机号失败:', error);
// 只在非自动触发时显示错误提示
if (triggerType !== 'auto') {
wx.showToast({
title: '保存失败,请重试',
icon: 'none'
});
}
} finally {
this.setData({ saving: false });
}
},
// 保存昵称到服务器
async saveNicknameToServer(nickname) {
this.setData({ saving: true });
try {
console.log('=== 开始保存昵称到服务器 ===');
// 如果传入了昵称参数,使用传入的昵称;否则使用当前数据中的昵称
const finalNickname = nickname || this.data.personInfo.nickName;
console.log('要保存的昵称:', finalNickname);
console.log('当前数据中的昵称:', this.data.personInfo.nickName);
// 更新本地数据
if (nickname && nickname !== this.data.personInfo.nickName) {
this.setData({
'personInfo.nickName': nickname
});
}
const userInfo = {
nickName: finalNickname,
avatarUrl: this.data.personInfo.avatarUrl,
gender: this.data.personInfo.gender
};
console.log('发送到服务器的用户信息:', userInfo);
const result = await weChatAuthService.updateUserProfile(userInfo);
console.log('服务器返回结果:', result);
// 保存成功后,重新获取用户信息验证是否真的更新了
console.log('=== 重新获取用户信息验证更新 ===');
this.fetchData();
this.setData({ saving: false });
Toast({
context: this,
selector: '#t-toast',
message: '昵称设置成功',
theme: 'success',
});
} catch (error) {
console.error('保存昵称失败:', error);
this.setData({ saving: false });
Toast({
context: this,
selector: '#t-toast',
message: '昵称设置失败,请重试',
theme: 'error',
});
}
},
// 兼容旧版本的头像选择方式 - 自动保存
async toModifyAvatar() {
try {
const tempFilePath = await new Promise((resolve, reject) => {
wx.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const { path, size } = res.tempFiles && res.tempFiles.length > 0 ? res.tempFiles[0] : {};
if (size <= 10485760) {
resolve(path);
} else {
reject({ errMsg: '图片大小超出限制,请重新上传' });
}
},
fail: (err) => reject(err),
});
});
// 更新本地显示
this.setData({
'personInfo.avatarUrl': tempFilePath,
});
// 自动保存头像
await this.saveAvatar(tempFilePath);
} catch (error) {
if (error.errMsg === 'chooseImage:fail cancel') return;
Toast({
context: this,
selector: '#t-toast',
message: error.errMsg || error.msg || '选择头像出错了',
theme: 'error',
});
}
},
// 切换账号登录
openUnbindConfirm() {
wx.showModal({
title: '切换账号',
content: '确定要切换到其他微信账号登录吗?',
success: (res) => {
if (res.confirm) {
// 清除本地存储的用户信息
wx.removeStorageSync('token');
wx.removeStorageSync('userInfo');
// 跳转到登录页面
wx.reLaunch({
url: '/pages/login/index'
});
}
}
});
},
// 页面卸载时清理定时器
onUnload() {
if (this.phoneInputTimer) {
clearTimeout(this.phoneInputTimer);
this.phoneInputTimer = null;
console.log('页面卸载,清理手机号输入定时器');
}
}
});

View File

@@ -0,0 +1,16 @@
{
"navigationBarTitleText": "个人资料",
"usingComponents": {
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-button": "tdesign-miniprogram/button/button",
"t-avatar": "tdesign-miniprogram/avatar/avatar",
"t-image": "/components/webp-image/index",
"t-dialog": "tdesign-miniprogram/dialog/dialog",
"t-toast": "tdesign-miniprogram/toast/toast",
"t-picker": "tdesign-miniprogram/picker/picker",
"t-picker-item": "tdesign-miniprogram/picker-item/picker-item",
"t-loading": "tdesign-miniprogram/loading/loading",
"t-select-picker": "../../usercenter/components/ui-select-picker/index"
}
}

View File

@@ -0,0 +1,112 @@
<view class="person-info">
<!-- 页面标题 -->
<view class="page-header">
<view class="header-title">个人信息</view>
<view class="header-subtitle">完善您的个人资料</view>
</view>
<!-- 信息设置卡片 -->
<view class="info-card">
<t-cell-group>
<!-- 微信头像设置 -->
<t-cell
title="微信头像"
center="{{true}}"
t-class-left="order-group__left"
>
<view slot="note" class="avatar-container">
<button
class="avatar-button"
open-type="chooseAvatar"
bind:chooseavatar="onChooseAvatar"
>
<t-avatar
image="{{personInfo.avatarUrl || defaultAvatarUrl}}"
class="avatar-display"
mode="aspectFill"
/>
<view class="avatar-mask">
<text class="avatar-text">点击更换微信头像</text>
</view>
</button>
</view>
</t-cell>
<!-- 微信昵称设置 -->
<t-cell title="微信昵称" t-class="t-cell-class">
<view slot="note" class="nickname-container">
<input
class="nickname-input"
type="nickname"
placeholder="请输入微信昵称"
value="{{personInfo.nickName}}"
bind:input="onNicknameChange"
bind:blur="saveNickname"
bind:confirm="saveNickname"
maxlength="20"
/>
</view>
</t-cell>
<!-- 性别设置 -->
<t-cell
title="性别"
t-class="t-cell-class"
t-class-left="order-group__left"
>
<view slot="note" class="gender-radio-container">
<radio-group bind:change="onGenderChange">
<label class="gender-radio-item">
<radio value="1" checked="{{personInfo.gender == 1}}" color="#667eea"/>
<text class="gender-text">男</text>
</label>
<label class="gender-radio-item">
<radio value="2" checked="{{personInfo.gender == 2}}" color="#667eea"/>
<text class="gender-text">女</text>
</label>
</radio-group>
</view>
</t-cell>
<!-- 手机号设置 -->
<t-cell
bordered="{{false}}"
title="手机号"
t-class="t-cell-class"
t-class-left="order-group__left"
>
<view slot="note" class="phone-input-container">
<input
class="phone-input"
type="number"
placeholder="请输入手机号"
value="{{displayPhoneNumber}}"
bind:input="onPhoneInput"
bind:blur="onPhoneBlur"
bind:confirm="onPhoneConfirm"
bind:focus="onPhoneFocus"
maxlength="11"
/>
</view>
</t-cell>
</t-cell-group>
</view>
<!-- 保存状态提示 -->
<view class="saving-status" wx:if="{{saving}}">
<t-loading theme="circular" size="40rpx" />
<text class="saving-text">正在保存...</text>
</view>
</view>
<!-- 切换账号按钮 -->
<view class="person-info__wrapper">
<view class="person-info__btn" bind:tap="openUnbindConfirm"> 切换账号登录 </view>
</view>
<!-- Toast 提示 -->
<t-toast id="t-toast" />

View File

@@ -0,0 +1,376 @@
:host {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
page {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
page view {
box-sizing: border-box;
}
/* 个人信息页面样式 */
.person-info {
padding: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
position: relative;
}
/* 页面标题样式 */
.page-header {
text-align: center;
padding: 80rpx 0 50rpx;
animation: fadeIn 0.8s ease-out;
}
.header-title {
font-size: 52rpx;
font-weight: 700;
color: #fff;
margin-bottom: 20rpx;
text-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.25);
letter-spacing: 2rpx;
}
.header-subtitle {
font-size: 30rpx;
color: rgba(255, 255, 255, 0.85);
font-weight: 400;
letter-spacing: 1rpx;
}
/* 信息卡片样式 */
.info-card {
background: #fff;
border-radius: 28rpx;
margin: 32rpx 24rpx;
box-shadow: 0 12rpx 40rpx rgba(102, 126, 234, 0.15);
overflow: hidden;
animation: slideUp 0.6s ease-out;
padding: 40rpx 32rpx 50rpx;
border: 1rpx solid rgba(102, 126, 234, 0.08);
}
/* 通用按钮样式 */
.person-info__btn {
width: 100%;
height: 96rpx;
background: rgba(255, 255, 255, 0.15);
border: 2rpx solid rgba(255, 255, 255, 0.3);
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #ffffff;
font-weight: 600;
margin-top: 32rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
backdrop-filter: blur(10rpx);
transition: all 0.3s ease;
}
.person-info__btn:active {
transform: translateY(2rpx);
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
background: rgba(255, 255, 255, 0.25);
}
.person-info__wrapper {
padding: 0 32rpx 60rpx;
}
/* 头像相关样式 */
.avatar-container {
position: relative;
width: 120rpx;
height: 120rpx;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-button {
position: relative;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
border: 3rpx solid #667eea;
background: transparent;
padding: 0;
box-shadow: 0 6rpx 20rpx rgba(102, 126, 234, 0.25);
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-button::after {
border: none;
}
.avatar-button:active {
transform: scale(0.96);
box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.35);
}
.avatarUrl {
width: 114rpx;
height: 114rpx;
border-radius: 50%;
object-fit: cover;
}
/* t-avatar 组件样式 */
.avatar-display {
width: 114rpx !important;
height: 114rpx !important;
border-radius: 50% !important;
overflow: hidden !important;
object-fit: cover !important;
}
.avatar-mask {
position: absolute;
top: 3rpx;
left: 3rpx;
width: calc(100% - 6rpx);
height: calc(100% - 6rpx);
background: linear-gradient(135deg, rgba(102, 126, 234, 0.85), rgba(118, 75, 162, 0.85));
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: all 0.3s ease;
backdrop-filter: blur(6rpx);
}
.avatar-button:active .avatar-mask {
opacity: 1;
}
.avatar-text {
color: #fff;
font-size: 20rpx;
text-align: center;
line-height: 1.3;
font-weight: 500;
font-weight: 600;
text-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.3);
}
/* 昵称输入框样式 */
.nickname-container {
display: flex;
flex-direction: column;
align-items: flex-end;
min-width: 300rpx;
}
.nickname-input {
width: 100%;
height: 76rpx;
padding: 0 20rpx;
border: 2rpx solid #e8eaed;
border-radius: 20rpx;
font-size: 28rpx;
color: #333;
background: #f8f9fa;
text-align: right;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.nickname-input:focus {
border-color: #667eea;
background: #fff;
box-shadow: 0 6rpx 20rpx rgba(102, 126, 234, 0.18);
transform: translateY(-1rpx);
outline: none;
}
.nickname-input::placeholder {
color: #aaa;
font-size: 26rpx;
}
/* 性别单选框样式 */
.gender-radio-container {
display: flex;
gap: 40rpx;
align-items: center;
}
.gender-radio-container radio-group {
display: flex;
flex-direction: row;
gap: 40rpx;
align-items: center;
}
.gender-radio-item {
display: flex;
align-items: center;
gap: 12rpx;
padding: 8rpx 0;
cursor: pointer;
}
.gender-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
/* 手机号输入框样式 */
.phone-input-container {
width: 100%;
display: flex;
justify-content: flex-end;
}
.phone-input {
width: 100%;
height: 76rpx;
padding: 0 20rpx;
border: 2rpx solid #e8eaed;
border-radius: 20rpx;
font-size: 28rpx;
color: #333;
background: #f8f9fa;
text-align: right;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.phone-input:focus {
border-color: #667eea;
background: #fff;
box-shadow: 0 6rpx 20rpx rgba(102, 126, 234, 0.18);
transform: translateY(-1rpx);
outline: none;
}
.phone-input::placeholder {
color: #aaa;
font-size: 26rpx;
}
/* 保存状态提示样式 */
.saving-status {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: linear-gradient(135deg, rgba(102, 126, 234, 0.96), rgba(118, 75, 162, 0.96));
color: #fff;
padding: 28rpx 56rpx;
border-radius: 60rpx;
font-size: 30rpx;
z-index: 1000;
backdrop-filter: blur(12rpx);
box-shadow: 0 12rpx 40rpx rgba(102, 126, 234, 0.35);
border: 1rpx solid rgba(255, 255, 255, 0.25);
animation: pulse 1.5s ease-in-out infinite;
}
.saving-text {
font-weight: 600;
letter-spacing: 1rpx;
}
/* Cell组件样式优化 */
.person-info .t-cell-class {
height: 120rpx;
background: transparent;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
}
.person-info .t-cell-class:last-child {
border-bottom: none;
}
/* 添加微妙的动画效果 */
.info-card {
animation: slideUp 0.6s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(80rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.page-header {
animation: fadeIn 0.8s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
/* 响应式设计 */
@media (max-width: 375px) {
.page-header {
padding: 50rpx 24rpx 30rpx;
}
.header-title {
font-size: 42rpx;
}
.header-subtitle {
font-size: 26rpx;
}
.info-card {
margin: 0 24rpx 24rpx;
}
.nickname-container {
min-width: 250rpx;
}
.avatar-container {
width: 120rpx;
height: 120rpx;
}
.avatar-button,
.avatar-display {
width: 120rpx !important;
height: 120rpx !important;
}
.person-info__wrapper {
padding: 0 24rpx 50rpx;
}
}