582 lines
12 KiB
Vue
582 lines
12 KiB
Vue
<template>
|
|
<view class="user-info-container page-container-with-bg">
|
|
<!-- 头像设置卡片 -->
|
|
<view class="avatar-card">
|
|
<view class="card-header">
|
|
<text class="card-title">头像设置</text>
|
|
</view>
|
|
<view class="avatar-section">
|
|
<view class="avatar-wrapper" @click="chooseAvatar">
|
|
<u-avatar
|
|
:src="userInfo.avatarUrl || ''"
|
|
:text="userInfo.nickName ? userInfo.nickName.charAt(0) : '👤'"
|
|
size="120"
|
|
shape="circle"
|
|
bg-color="linear-gradient(135deg, #FF8A80, #FFB6C1)"
|
|
color="white"
|
|
font-size="50"
|
|
></u-avatar>
|
|
<view class="avatar-edit-icon">
|
|
<text class="edit-text">📷</text>
|
|
</view>
|
|
</view>
|
|
<view class="avatar-tips">
|
|
<text class="tips-text">点击更换头像</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 基本信息卡片 -->
|
|
<view class="info-card">
|
|
<view class="card-header">
|
|
<text class="card-title">基本信息</text>
|
|
</view>
|
|
<view class="info-list">
|
|
<view class="info-item">
|
|
<view class="info-label">
|
|
<text class="label-text">昵称</text>
|
|
</view>
|
|
<view class="info-input">
|
|
<u-input
|
|
v-model="userInfo.nickName"
|
|
placeholder="请输入昵称"
|
|
border="none"
|
|
:custom-style="inputStyle"
|
|
@change="onNickNameChange"
|
|
></u-input>
|
|
</view>
|
|
</view>
|
|
<view class="info-item">
|
|
<view class="info-label">
|
|
<text class="label-text">性别</text>
|
|
</view>
|
|
<view class="info-value" @click="showGenderPicker">
|
|
<text class="value-text">{{ userInfo.gender || '请选择' }}</text>
|
|
<text class="arrow-icon">→</text>
|
|
</view>
|
|
</view>
|
|
<view class="info-item">
|
|
<view class="info-label">
|
|
<text class="label-text">生日</text>
|
|
</view>
|
|
<view class="info-value" @click="showDatePicker">
|
|
<text class="value-text">{{ userInfo.birthday || '请选择' }}</text>
|
|
<text class="arrow-icon">→</text>
|
|
</view>
|
|
</view>
|
|
<view class="info-item">
|
|
<view class="info-label">
|
|
<text class="label-text">地区</text>
|
|
</view>
|
|
<view class="info-value" @click="showRegionPicker">
|
|
<text class="value-text">{{ userInfo.region || '请选择' }}</text>
|
|
<text class="arrow-icon">→</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 个人简介卡片 -->
|
|
<view class="bio-card">
|
|
<view class="card-header">
|
|
<text class="card-title">个人简介</text>
|
|
</view>
|
|
<view class="bio-section">
|
|
<u-textarea
|
|
v-model="userInfo.bio"
|
|
placeholder="介绍一下自己和你的宠物吧..."
|
|
:maxlength="200"
|
|
count
|
|
:custom-style="textareaStyle"
|
|
@change="onBioChange"
|
|
></u-textarea>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 账号信息卡片 -->
|
|
<view class="account-card">
|
|
<view class="card-header">
|
|
<text class="card-title">账号信息</text>
|
|
</view>
|
|
<view class="account-list">
|
|
<view class="account-item">
|
|
<view class="account-label">
|
|
<text class="label-text">注册时间</text>
|
|
</view>
|
|
<view class="account-value">
|
|
<text class="value-text">{{ formatDate(userInfo.registerTime) }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="account-item">
|
|
<view class="account-label">
|
|
<text class="label-text">使用天数</text>
|
|
</view>
|
|
<view class="account-value">
|
|
<text class="value-text">{{ loginDays }} 天</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 保存按钮 -->
|
|
<view class="save-section">
|
|
<u-button
|
|
type="primary"
|
|
:custom-style="buttonStyle"
|
|
@click="saveUserInfo"
|
|
:loading="saving"
|
|
>
|
|
{{ saving ? '保存中...' : '保存修改' }}
|
|
</u-button>
|
|
</view>
|
|
|
|
<!-- 性别选择器 -->
|
|
<u-picker
|
|
:show="showGender"
|
|
:columns="genderColumns"
|
|
@confirm="onGenderConfirm"
|
|
@cancel="showGender = false"
|
|
></u-picker>
|
|
|
|
<!-- 日期选择器 -->
|
|
<u-datetime-picker
|
|
:show="showDate"
|
|
v-model="selectedDate"
|
|
mode="date"
|
|
@confirm="onDateConfirm"
|
|
@cancel="showDate = false"
|
|
></u-datetime-picker>
|
|
|
|
<!-- 地区选择器 -->
|
|
<u-picker
|
|
:show="showRegion"
|
|
:columns="regionColumns"
|
|
@confirm="onRegionConfirm"
|
|
@cancel="showRegion = false"
|
|
></u-picker>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { reactive, ref, onMounted, computed } from 'vue'
|
|
|
|
export default {
|
|
name: 'UserInfoPage',
|
|
setup() {
|
|
// 响应式数据
|
|
const userInfo = reactive({
|
|
nickName: '',
|
|
avatarUrl: '',
|
|
gender: '',
|
|
birthday: '',
|
|
region: '',
|
|
bio: '',
|
|
registerTime: ''
|
|
})
|
|
|
|
const loginDays = ref(0)
|
|
const saving = ref(false)
|
|
|
|
// 选择器状态
|
|
const showGender = ref(false)
|
|
const showDate = ref(false)
|
|
const showRegion = ref(false)
|
|
const selectedDate = ref(Date.now())
|
|
|
|
// 选择器数据
|
|
const genderColumns = ref([
|
|
['男', '女', '保密']
|
|
])
|
|
|
|
const regionColumns = ref([
|
|
['北京市', '上海市', '广州市', '深圳市', '杭州市', '成都市', '武汉市', '西安市', '南京市', '重庆市']
|
|
])
|
|
|
|
// 样式配置
|
|
const inputStyle = {
|
|
fontSize: '30rpx',
|
|
color: '#333333'
|
|
}
|
|
|
|
const textareaStyle = {
|
|
fontSize: '28rpx',
|
|
color: '#333333'
|
|
}
|
|
|
|
const buttonStyle = {
|
|
background: 'linear-gradient(135deg, #FF8A80, #FFB6C1)',
|
|
borderRadius: '24rpx',
|
|
height: '88rpx',
|
|
fontSize: '32rpx'
|
|
}
|
|
|
|
// 生命周期
|
|
onMounted(() => {
|
|
loadUserInfo()
|
|
})
|
|
|
|
// 方法定义
|
|
const loadUserInfo = () => {
|
|
try {
|
|
const savedUserInfo = uni.getStorageSync('userInfo') || {}
|
|
Object.assign(userInfo, {
|
|
nickName: savedUserInfo.nickName || '',
|
|
avatarUrl: savedUserInfo.avatarUrl || '',
|
|
gender: savedUserInfo.gender || '',
|
|
birthday: savedUserInfo.birthday || '',
|
|
region: savedUserInfo.region || '',
|
|
bio: savedUserInfo.bio || '',
|
|
registerTime: savedUserInfo.registerTime || new Date().toISOString()
|
|
})
|
|
|
|
calculateLoginDays()
|
|
} catch (error) {
|
|
console.error('加载用户信息失败:', error)
|
|
}
|
|
}
|
|
|
|
const calculateLoginDays = () => {
|
|
const loginDate = uni.getStorageSync('loginDate')
|
|
if (loginDate) {
|
|
const now = new Date()
|
|
const login = new Date(loginDate)
|
|
const diffTime = Math.abs(now - login)
|
|
loginDays.value = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
|
}
|
|
}
|
|
|
|
const chooseAvatar = () => {
|
|
uni.chooseImage({
|
|
count: 1,
|
|
sizeType: ['compressed'],
|
|
sourceType: ['album', 'camera'],
|
|
success: (res) => {
|
|
userInfo.avatarUrl = res.tempFilePaths[0]
|
|
uni.showToast({
|
|
title: '头像已更新',
|
|
icon: 'success'
|
|
})
|
|
},
|
|
fail: (err) => {
|
|
console.error('选择头像失败:', err)
|
|
uni.showToast({
|
|
title: '选择头像失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
const showGenderPicker = () => {
|
|
showGender.value = true
|
|
}
|
|
|
|
const showDatePicker = () => {
|
|
showDate.value = true
|
|
}
|
|
|
|
const showRegionPicker = () => {
|
|
showRegion.value = true
|
|
}
|
|
|
|
const onGenderConfirm = (e) => {
|
|
userInfo.gender = e.value[0]
|
|
showGender.value = false
|
|
}
|
|
|
|
const onDateConfirm = (e) => {
|
|
const date = new Date(e.value)
|
|
userInfo.birthday = date.toISOString().split('T')[0]
|
|
showDate.value = false
|
|
}
|
|
|
|
const onRegionConfirm = (e) => {
|
|
userInfo.region = e.value[0]
|
|
showRegion.value = false
|
|
}
|
|
|
|
const onNickNameChange = (value) => {
|
|
userInfo.nickName = value
|
|
}
|
|
|
|
const onBioChange = (value) => {
|
|
userInfo.bio = value
|
|
}
|
|
|
|
const saveUserInfo = async () => {
|
|
if (!userInfo.nickName.trim()) {
|
|
uni.showToast({
|
|
title: '请输入昵称',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
saving.value = true
|
|
|
|
try {
|
|
// 保存到本地存储
|
|
uni.setStorageSync('userInfo', userInfo)
|
|
|
|
// 模拟网络请求延迟
|
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
|
|
uni.showToast({
|
|
title: '保存成功',
|
|
icon: 'success'
|
|
})
|
|
|
|
// 返回上一页
|
|
setTimeout(() => {
|
|
uni.navigateBack()
|
|
}, 1500)
|
|
|
|
} catch (error) {
|
|
console.error('保存用户信息失败:', error)
|
|
uni.showToast({
|
|
title: '保存失败',
|
|
icon: 'none'
|
|
})
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
const formatDate = (dateString) => {
|
|
if (!dateString) return '未设置'
|
|
const date = new Date(dateString)
|
|
return date.toLocaleDateString('zh-CN')
|
|
}
|
|
|
|
return {
|
|
userInfo,
|
|
loginDays,
|
|
saving,
|
|
showGender,
|
|
showDate,
|
|
showRegion,
|
|
selectedDate,
|
|
genderColumns,
|
|
regionColumns,
|
|
inputStyle,
|
|
textareaStyle,
|
|
buttonStyle,
|
|
chooseAvatar,
|
|
showGenderPicker,
|
|
showDatePicker,
|
|
showRegionPicker,
|
|
onGenderConfirm,
|
|
onDateConfirm,
|
|
onRegionConfirm,
|
|
onNickNameChange,
|
|
onBioChange,
|
|
saveUserInfo,
|
|
formatDate
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.user-info-container {
|
|
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
|
min-height: 100vh;
|
|
padding-bottom: 40rpx;
|
|
}
|
|
|
|
/* 通用卡片样式 */
|
|
.avatar-card,
|
|
.info-card,
|
|
.bio-card,
|
|
.account-card {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20rpx);
|
|
margin: 0 30rpx 24rpx 30rpx;
|
|
border-radius: 24rpx;
|
|
padding: 32rpx;
|
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
|
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
|
|
|
.card-header {
|
|
margin-bottom: 24rpx;
|
|
|
|
.card-title {
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #333333;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 头像设置卡片 */
|
|
.avatar-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
.avatar-wrapper {
|
|
position: relative;
|
|
margin-bottom: 16rpx;
|
|
|
|
.avatar-edit-icon {
|
|
position: absolute;
|
|
bottom: 8rpx;
|
|
right: 8rpx;
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
|
.edit-text {
|
|
font-size: 24rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.avatar-tips {
|
|
.tips-text {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 基本信息卡片 */
|
|
.info-list {
|
|
.info-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 24rpx 0;
|
|
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.info-label {
|
|
width: 160rpx;
|
|
|
|
.label-text {
|
|
font-size: 30rpx;
|
|
color: #333333;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
.info-input {
|
|
flex: 1;
|
|
}
|
|
|
|
.info-value {
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
|
|
.value-text {
|
|
font-size: 30rpx;
|
|
color: #333333;
|
|
}
|
|
|
|
.arrow-icon {
|
|
font-size: 24rpx;
|
|
color: #FF8A80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 个人简介卡片 */
|
|
.bio-section {
|
|
.bio-textarea {
|
|
width: 100%;
|
|
min-height: 200rpx;
|
|
padding: 20rpx;
|
|
border: 1rpx solid rgba(255, 138, 128, 0.2);
|
|
border-radius: 16rpx;
|
|
background: rgba(255, 255, 255, 0.5);
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
line-height: 1.6;
|
|
}
|
|
}
|
|
|
|
/* 账号信息卡片 */
|
|
.account-list {
|
|
.account-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 24rpx 0;
|
|
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.account-label {
|
|
.label-text {
|
|
font-size: 30rpx;
|
|
color: #333333;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
.account-value {
|
|
.value-text {
|
|
font-size: 30rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 保存按钮 */
|
|
.save-section {
|
|
margin: 40rpx 30rpx 0 30rpx;
|
|
}
|
|
|
|
/* 响应式设计 */
|
|
@media (max-width: 375px) {
|
|
.user-info-container {
|
|
.avatar-card,
|
|
.info-card,
|
|
.bio-card,
|
|
.account-card {
|
|
margin: 0 20rpx 20rpx 20rpx;
|
|
padding: 24rpx;
|
|
}
|
|
|
|
.save-section {
|
|
margin: 32rpx 20rpx 0 20rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 动画效果 */
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20rpx);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.user-info-container > view {
|
|
animation: fadeIn 0.5s ease-out;
|
|
}
|
|
|
|
.user-info-container > view:nth-child(1) { animation-delay: 0.1s; }
|
|
.user-info-container > view:nth-child(2) { animation-delay: 0.2s; }
|
|
.user-info-container > view:nth-child(3) { animation-delay: 0.3s; }
|
|
.user-info-container > view:nth-child(4) { animation-delay: 0.4s; }
|
|
.user-info-container > view:nth-child(5) { animation-delay: 0.5s; }
|
|
</style>
|