四零语境前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

368 lines
8.8 KiB

<template>
<view class="profile-container">
<!-- 个人信息表单 -->
<view class="form-container">
<view class="form-title">个人信息</view>
<!-- 昵称 -->
<view class="form-item">
<view class="label">
<text class="required">*</text>
<text>昵称</text>
</view>
<uv-input
v-model="userInfo.name"
placeholder="请输入昵称"
type="nickname"
:customStyle="inputStyle"
border="bottom"
></uv-input>
</view>
<!-- 电话 -->
<view class="form-item">
<view class="label">
<text class="required">*</text>
<text>电话</text>
</view>
<uv-input
v-model="userInfo.phone"
placeholder="请输入手机号"
:customStyle="inputStyle"
border="bottom"
type="number"
></uv-input>
</view>
<!-- 头像 -->
<view class="form-item">
<view class="label">
<text class="required">*</text>
<text>头像</text>
</view>
<!-- #ifdef MP-WEIXIN -->
<button class="avatar-container" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<view
v-if="userInfo.avatar === 'undefined'"
class="avatar-image"
>
<uv-icon name="camera" size="40" color="white"></uv-icon>
</view>
<image
v-else
:src="userInfo.avatar || '/static/default-avatar.png'"
class="avatar-image"
mode="aspectFill"
></image>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button class="avatar-container" @click="chooseImageH5">
<view
v-if="userInfo.avatar === 'undefined'"
class="avatar-image"
>
<uv-icon name="camera" size="40" color="white"></uv-icon>
</view>
<image
v-else
:src="userInfo.avatar || '/static/default-avatar.png'"
class="avatar-image"
mode="aspectFill"
></image>
</button>
<!-- #endif -->
</view>
</view>
<!-- 固定底部保存按钮 -->
<view class="save-button-container">
<uv-button
@click="saveProfile"
:customStyle="saveButtonStyle"
shape="circle"
>
保存
</uv-button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
userInfo: {
avatar: 'undefined',
name: '',
phone: ''
},
saveButtonStyle: {
backgroundColor: '#06DADC',
borderRadius: '41rpx',
height: '94rpx',
width: '594rpx',
border: 'none',
color: '#fff',
fontSize: '32rpx',
fontWeight: '500'
},
inputStyle: {
backgroundColor: '#fff',
borderRadius: '12rpx',
padding: '0 -20rpx',
fontSize: '28rpx'
}
}
},
methods: {
// 选择头像并上传到OSS
async onChooseAvatar(e) {
console.log('选择头像回调', e);
if (e.detail.avatarUrl) {
try {
// 显示上传中提示
uni.showLoading({ title: '上传头像中...' });
// 构造文件对象
const file = {
path: e.detail.avatarUrl,
tempFilePath: e.detail.avatarUrl
};
// 上传到OSS
const uploadResult = await this.$utils.uploadImage(file);
uni.hideLoading();
if (uploadResult.success) {
// 上传成功,更新头像URL
this.userInfo.avatar = uploadResult.url;
console.log('头像上传成功', uploadResult.url);
uni.showToast({
title: '头像上传成功',
icon: 'success'
});
} else {
}
} catch (error) {
uni.hideLoading();
console.error('头像上传异常:', error);
// 异常情况下使用本地头像
// this.userInfo.avatar = e.detail.avatarUrl;
uni.showToast({
title: '头像处理异常,使用本地头像',
icon: 'none'
});
}
} else {
uni.showToast({
title: '头像选择失败',
icon: 'none'
});
}
},
// 公众号/H5 选择图片并上传头像
// #ifndef MP-WEIXIN
async chooseImageH5() {
try {
const res = await uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera']
})
const filePath = (res.tempFilePaths && res.tempFilePaths[0])
|| (res.tempFiles && res.tempFiles[0] && (res.tempFiles[0].path || res.tempFiles[0].tempFilePath))
if (!filePath) {
uni.showToast({ title: '未选择图片', icon: 'none' })
return
}
uni.showLoading({ title: '上传头像中...' })
const file = { path: filePath, tempFilePath: filePath }
const uploadResult = await this.$utils.uploadImage(file)
uni.hideLoading()
if (uploadResult && uploadResult.success) {
this.userInfo.avatar = uploadResult.url
uni.showToast({ title: '头像上传成功', icon: 'success' })
} else {
// 上传失败则先本地显示
this.userInfo.avatar = filePath
uni.showToast({ title: '头像已选择', icon: 'none' })
}
} catch (error) {
uni.hideLoading()
console.error('选择/上传头像异常:', error)
uni.showToast({ title: '头像处理异常', icon: 'none' })
}
},
// #endif
// 保存资料
async saveProfile() {
if (!this.userInfo.name?.trim()) {
uni.showToast({
title: '请输入昵称',
icon: 'none'
})
return
}
if (!this.userInfo.phone?.trim()) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
// 简单的手机号验证
const phoneReg = /^1[3-9]\d{9}$/
if (!phoneReg.test(this.userInfo.phone)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
// TODO: 调用API保存用户信息
const res = await this.$api.login.updateUserInfo({
avatar: this.userInfo.avatar,
name: this.userInfo.name,
phone: this.userInfo.phone
})
if (res.code === 200) {
uni.showToast({
title: '保存成功',
icon: 'success'
})
// 延迟返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
},
// 获取个人信息
async getProfile() {
const res = await this.$api.login.getUserInfo()
if (res.code === 200) {
this.userInfo = res.result
this.$store.dispatch('updateUserInfo', this.userInfo)
}
}
},
onLoad() {
this.getProfile()
// 3秒后隐藏遮罩层
}
}
</script>
<style lang="scss" scoped>
.profile-container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 40rpx 32rpx 200rpx;
.form-container {
background: #fff;
border-radius: 20rpx;
padding: 40rpx 32rpx;
.form-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 60rpx;
}
.form-item {
margin-bottom: 60rpx;
&:last-child {
margin-bottom: 0;
}
.label {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
color: #333;
.required {
color: #ff4757;
margin-right: 8rpx;
}
}
}
.avatar-container {
position: relative;
width: 200rpx;
height: 200rpx;
border-radius: 16rpx;
overflow: hidden;
display: block;
text-align: left;
margin: 0;
padding: 0;
background: none;
border: none;
.avatar-image {
width: 100%;
height: 100%;
background: #00000080;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24rpx;
transition: opacity 1s ease-out;
&.fade-out {
opacity: 0;
}
}
}
}
.save-button-container {
position: fixed;
bottom: 60rpx;
left: 50%;
transform: translateX(-50%);
z-index: 999;
}
}
</style>