408 lines
8.0 KiB
Vue
408 lines
8.0 KiB
Vue
<template>
|
||
<view class="content column">
|
||
|
||
<view class="header">
|
||
<image src="/static/back.png" class="back-icon" @click="goBack()" mode=""></image>
|
||
<text class="header-title">修改信息</text>
|
||
<view class="header-spacer"></view>
|
||
</view>
|
||
|
||
<view class="form-container">
|
||
|
||
<view class="form-row">
|
||
<text class="form-label">我的UID</text>
|
||
<text class="form-value">{{ user.id }}</text>
|
||
</view>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<view class="form-row avatar-row">
|
||
<text class="form-label">我的头像</text>
|
||
<button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
|
||
<image :src="user.avatarImage" class="avatar-image" mode="">
|
||
</image>
|
||
</button>
|
||
</view>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<view class="form-row">
|
||
<text class="form-label">我的昵称</text>
|
||
<input type="nickname" v-model="user.nickName" class="form-input" />
|
||
</view>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<view class="form-row">
|
||
<text class="form-label">我的年龄</text>
|
||
<text class="form-value">{{ calculatedAge }}岁</text>
|
||
</view>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<view class="form-row">
|
||
<text class="form-label">我的性别</text>
|
||
<picker mode="selector" :value="genderIndex" :range="GENDER_OPTIONS" @change="onGenderChange">
|
||
<view class="picker-text">
|
||
{{ currentGender }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<view class="form-row">
|
||
<text class="form-label">我的生日</text>
|
||
<picker mode="date" :value="user.birthday" @change="onBirthdayChange">
|
||
<view class="picker-text">
|
||
{{ user.birthday || '请选择生日' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="divider"></view>
|
||
</view>
|
||
|
||
<!-- 保存按钮 -->
|
||
<view class="save-button-container">
|
||
<button class="save-button" @click="saveUserInfo" :disabled="isSaving">
|
||
{{ isSaving ? '保存中...' : '保存修改' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { userInfo, updateUserInfo } from '@/common/server/user'
|
||
|
||
// 常量配置
|
||
const GENDER_OPTIONS = ['男', '女']
|
||
|
||
// 响应式数据
|
||
const genderIndex = ref(0)
|
||
const isSaving = ref(false)
|
||
|
||
// 用户信息
|
||
const user = ref({
|
||
avatarImage: userInfo.value.avatarImage || '',
|
||
nickName: userInfo.value.nickName || '',
|
||
id: userInfo.value.id || '',
|
||
sex: userInfo.value.sex == 1 ? '男' : '女',
|
||
birthday: userInfo.value.birthday ?? "1990-01-01"
|
||
})
|
||
|
||
// 计算属性
|
||
const currentGender = computed(() => GENDER_OPTIONS[genderIndex.value])
|
||
|
||
// 根据生日计算年龄
|
||
const calculatedAge = computed(() => {
|
||
if (!user.value.birthday) return 0
|
||
|
||
const today = new Date()
|
||
const birthDate = new Date(user.value.birthday)
|
||
|
||
let age = today.getFullYear() - birthDate.getFullYear()
|
||
const monthDiff = today.getMonth() - birthDate.getMonth()
|
||
|
||
// 如果还没到生日,年龄减1
|
||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||
age--
|
||
}
|
||
|
||
return age
|
||
})
|
||
|
||
// 初始化picker索引
|
||
const initializePickerIndexes = () => {
|
||
// 设置性别索引
|
||
genderIndex.value = userInfo.value.sex === '女' ? 1 : 0
|
||
}
|
||
|
||
onMounted(() => {
|
||
console.log("userInfo", userInfo.value)
|
||
initializePickerIndexes()
|
||
})
|
||
// 方法
|
||
const goBack = () => {
|
||
uni.navigateBack({ delta: 1 })
|
||
}
|
||
|
||
/**
|
||
* 选择头像
|
||
*/
|
||
const onChooseAvatar = (e) => {
|
||
const { avatarUrl } = e?.detail || {}
|
||
console.log("avatarUrl", avatarUrl);
|
||
|
||
if (avatarUrl) {
|
||
convertToBase64(avatarUrl)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 性别选择变化
|
||
*/
|
||
const onGenderChange = (e) => {
|
||
const selectedIndex = e.detail.value
|
||
genderIndex.value = selectedIndex
|
||
user.value.sex = GENDER_OPTIONS[selectedIndex]
|
||
}
|
||
|
||
|
||
/**
|
||
* 生日选择变化
|
||
*/
|
||
const onBirthdayChange = (e) => {
|
||
user.value.birthday = e.detail.value
|
||
}
|
||
|
||
/**
|
||
* 将图片转换为Base64
|
||
*/
|
||
const convertToBase64 = (filePath) => {
|
||
if (!filePath) {
|
||
showErrorToast('图片路径无效')
|
||
return
|
||
}
|
||
|
||
uni.getFileSystemManager().readFile({
|
||
filePath,
|
||
encoding: "base64",
|
||
success: (res) => {
|
||
user.value.avatarImage = `data:image/png;base64,${res.data}`
|
||
},
|
||
fail: (err) => {
|
||
console.error("读取文件失败:", err)
|
||
showErrorToast("图片处理失败")
|
||
},
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 保存用户信息
|
||
*/
|
||
const saveUserInfo = async () => {
|
||
if (isSaving.value) return
|
||
|
||
// 验证必填字段
|
||
if (!user.value.nickName.trim()) {
|
||
showErrorToast('请输入昵称')
|
||
return
|
||
}
|
||
|
||
if (!user.value.birthday) {
|
||
showErrorToast('请选择生日')
|
||
return
|
||
}
|
||
|
||
isSaving.value = true
|
||
|
||
try {
|
||
// 准备保存的数据
|
||
const saveData = {
|
||
avatarImage: user.value.avatarImage,
|
||
nickName: user.value.nickName.trim(),
|
||
sex: user.value.sex === '男' ? 1 : 2,
|
||
birthday: user.value.birthday,
|
||
}
|
||
|
||
console.log('保存用户信息:', saveData)
|
||
|
||
// 这里应该调用实际的API保存数据
|
||
// await updateUserInfo(saveData)
|
||
//async (nickName, avatar, sex, birthday)
|
||
await updateUserInfo(user.value.nickName.trim(), user.value.avatarImage,
|
||
user.value.sex === '男' ? 1 : 2,
|
||
user.value.birthday
|
||
);
|
||
// 模拟API调用延迟
|
||
// await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
// 更新全局用户信息
|
||
// Object.assign(userInfo.value, saveData)
|
||
|
||
uni.showToast({
|
||
title: '保存成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
// 延迟返回上一页
|
||
setTimeout(() => {
|
||
uni.navigateBack({ delta: 1 })
|
||
}, 1500)
|
||
|
||
} catch (error) {
|
||
console.error('保存失败:', error)
|
||
showErrorToast('保存失败,请重试')
|
||
} finally {
|
||
isSaving.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示错误提示
|
||
*/
|
||
const showErrorToast = (message) => {
|
||
uni.showToast({
|
||
title: message,
|
||
icon: "none",
|
||
duration: 2000
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
// 变量定义
|
||
$primary-color: #333;
|
||
$border-color: antiquewhite;
|
||
$font-size-normal: 26rpx;
|
||
$font-size-title: 30rpx;
|
||
$avatar-size: 70rpx;
|
||
$border-radius: 50%;
|
||
$container-width: 90%;
|
||
$header-margin-top: 100rpx;
|
||
$form-margin-top: 80rpx;
|
||
$row-margin-top: 40rpx;
|
||
$divider-margin-top: 20rpx;
|
||
|
||
.content {
|
||
width: 100%;
|
||
height: 100vh;
|
||
}
|
||
|
||
// 头部样式
|
||
.header {
|
||
width: $container-width;
|
||
margin: $header-margin-top auto 0;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.back-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
}
|
||
|
||
.header-title {
|
||
font-size: $font-size-title;
|
||
}
|
||
|
||
.header-spacer {
|
||
width: 40rpx;
|
||
}
|
||
|
||
// 表单容器样式
|
||
.form-container {
|
||
width: $container-width;
|
||
margin: $form-margin-top auto 0;
|
||
}
|
||
|
||
// 表单行样式
|
||
.form-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 100%;
|
||
margin-top: $row-margin-top;
|
||
font-size: $font-size-normal;
|
||
|
||
&.avatar-row {
|
||
margin-top: $divider-margin-top;
|
||
}
|
||
}
|
||
|
||
.form-label {
|
||
font-size: $font-size-normal;
|
||
}
|
||
|
||
.form-value {
|
||
font-size: $font-size-normal;
|
||
}
|
||
|
||
.form-input {
|
||
text-align: right;
|
||
font-size: $font-size-normal;
|
||
}
|
||
|
||
// 分割线样式
|
||
.divider {
|
||
width: 100%;
|
||
height: 1rpx;
|
||
background-color: $border-color;
|
||
margin-top: $divider-margin-top;
|
||
}
|
||
|
||
// 头像相关样式
|
||
.avatar-wrapper {
|
||
margin: 0;
|
||
float: right;
|
||
border-radius: 128rpx;
|
||
overflow: hidden;
|
||
border: 0;
|
||
background-color: transparent;
|
||
padding: 0;
|
||
line-height: normal;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
|
||
.avatar {
|
||
width: 170rpx;
|
||
height: 160rpx;
|
||
border-radius: $border-radius;
|
||
}
|
||
}
|
||
|
||
.avatar-image {
|
||
width: $avatar-size;
|
||
height: $avatar-size;
|
||
background-color: aquamarine;
|
||
border-radius: $border-radius;
|
||
}
|
||
|
||
// Picker样式
|
||
.picker-text {
|
||
display: flex;
|
||
align-items: center;
|
||
color: $primary-color;
|
||
font-size: $font-size-normal;
|
||
}
|
||
|
||
// 保存按钮样式
|
||
.save-button-container {
|
||
width: $container-width;
|
||
margin: 60rpx auto 0;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.save-button {
|
||
width: 100%;
|
||
height: 80rpx;
|
||
background: linear-gradient(135deg, #F36903 0%, #E55A00 100%);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 40rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 20rpx rgba(243, 105, 3, 0.3);
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
transform: translateY(2rpx);
|
||
box-shadow: 0 8rpx 20rpx rgba(243, 105, 3, 0.3);
|
||
}
|
||
|
||
&:disabled {
|
||
background: #ccc;
|
||
box-shadow: none;
|
||
transform: none;
|
||
}
|
||
}
|
||
</style> |