433 lines
10 KiB
Vue
433 lines
10 KiB
Vue
<template>
|
||
<view class="personal-page">
|
||
<!-- 顶部背景图 -->
|
||
<view class="top-bg">
|
||
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
|
||
</view>
|
||
|
||
<!-- 自定义导航栏 -->
|
||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="navbar-content">
|
||
<view class="navbar-back" @click="handleBack">
|
||
<text class="back-icon">‹</text>
|
||
</view>
|
||
<text class="navbar-title">个人资料</text>
|
||
<view class="navbar-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 头像区域 -->
|
||
<view class="avatar-section" :style="{ marginTop: (statusBarHeight + 44) + 'px' }">
|
||
<view class="avatar-wrapper" @click="handleChangeAvatar">
|
||
<image
|
||
class="avatar-img"
|
||
:src="userInfo.avatar || defaultAvatar"
|
||
mode="aspectFill"
|
||
/>
|
||
<view class="avatar-edit-icon">
|
||
<text>✎</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 表单区域 -->
|
||
<view class="form-section">
|
||
<view class="form-item">
|
||
<text class="form-label">用户名</text>
|
||
<view class="form-input">
|
||
<input
|
||
type="text"
|
||
:value="nickname"
|
||
@input="e => nickname = e.detail.value"
|
||
placeholder="请输入用户名"
|
||
maxlength="20"
|
||
class="input-field"
|
||
/>
|
||
</view>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="form-label">相亲编号</text>
|
||
<view class="form-input disabled" @click="handleCopyXiangQinNo">
|
||
<text>{{ userInfo.xiangQinNo || '未设置' }}</text>
|
||
<text class="copy-icon">复制</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 保存按钮 -->
|
||
<view class="btn-section">
|
||
<button class="btn-save" @click="handleSave">保存</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { ref, computed, onMounted, watch } from 'vue'
|
||
import { useUserStore } from '@/store/user.js'
|
||
import { updateAvatar, updateNickname } from '@/api/user.js'
|
||
import { getMyProfile } from '@/api/profile.js'
|
||
|
||
export default {
|
||
name: 'PersonalPage',
|
||
setup() {
|
||
const userStore = useUserStore()
|
||
const defaultAvatar = '/static/logo.png'
|
||
const statusBarHeight = ref(20)
|
||
const nickname = ref('')
|
||
|
||
const userInfo = computed(() => ({
|
||
avatar: userStore.avatar,
|
||
xiangQinNo: userStore.xiangQinNo,
|
||
nickname: userStore.nickname
|
||
}))
|
||
|
||
// 监听 store 中的 nickname 变化,同步到本地
|
||
watch(() => userStore.nickname, (newVal) => {
|
||
if (newVal && !nickname.value) {
|
||
nickname.value = newVal
|
||
}
|
||
}, { immediate: true })
|
||
|
||
// 获取系统信息
|
||
const getSystemInfo = () => {
|
||
uni.getSystemInfo({
|
||
success: (res) => {
|
||
statusBarHeight.value = res.statusBarHeight || 20
|
||
}
|
||
})
|
||
}
|
||
|
||
// 返回
|
||
const handleBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
// 更换头像
|
||
const handleChangeAvatar = () => {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sizeType: ['compressed'],
|
||
sourceType: ['album', 'camera'],
|
||
success: async (res) => {
|
||
const tempFilePath = res.tempFilePaths[0]
|
||
|
||
uni.showLoading({ title: '上传中...' })
|
||
|
||
try {
|
||
// 上传图片(使用AppApi地址)
|
||
const uploadRes = await new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api'}/app/upload`,
|
||
filePath: tempFilePath,
|
||
name: 'file',
|
||
header: {
|
||
Authorization: `Bearer ${userStore.token}`
|
||
},
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
resolve(JSON.parse(res.data))
|
||
} else {
|
||
reject(new Error('上传失败'))
|
||
}
|
||
},
|
||
fail: reject
|
||
})
|
||
})
|
||
|
||
if (uploadRes.code === 0 && uploadRes.data?.url) {
|
||
// 调用后端API保存头像到数据库
|
||
const saveRes = await updateAvatar(uploadRes.data.url)
|
||
if (saveRes && saveRes.code === 0) {
|
||
userStore.updateUserInfo({ avatar: uploadRes.data.url })
|
||
uni.showToast({ title: '头像更新成功', icon: 'success' })
|
||
} else {
|
||
uni.showToast({ title: saveRes?.message || '保存失败', icon: 'none' })
|
||
}
|
||
} else {
|
||
uni.showToast({ title: uploadRes.message || '上传失败', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
console.error('上传头像失败:', error)
|
||
uni.showToast({ title: '上传失败', icon: 'none' })
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 复制相亲编号
|
||
const handleCopyXiangQinNo = () => {
|
||
const xiangQinNo = userStore.xiangQinNo
|
||
if (!xiangQinNo) {
|
||
uni.showToast({ title: '暂无编号', icon: 'none' })
|
||
return
|
||
}
|
||
uni.setClipboardData({
|
||
data: xiangQinNo,
|
||
success: () => {
|
||
uni.showToast({ title: '已复制', icon: 'success' })
|
||
}
|
||
})
|
||
}
|
||
|
||
// 保存
|
||
const handleSave = async () => {
|
||
// 检查昵称是否有变化
|
||
if (nickname.value && nickname.value !== userStore.nickname) {
|
||
uni.showLoading({ title: '保存中...' })
|
||
try {
|
||
const res = await updateNickname(nickname.value)
|
||
if (res && res.code === 0) {
|
||
userStore.updateUserInfo({ nickname: nickname.value })
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1000)
|
||
} else {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: res?.message || '保存失败', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('保存失败:', error)
|
||
uni.showToast({ title: '保存失败', icon: 'none' })
|
||
}
|
||
} else {
|
||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1000)
|
||
}
|
||
}
|
||
|
||
// 初始化昵称 - 从API获取最新数据
|
||
const initNickname = async () => {
|
||
userStore.restoreFromStorage()
|
||
|
||
// 从 API 获取用户资料
|
||
try {
|
||
const res = await getMyProfile()
|
||
if (res && res.code === 0 && res.data) {
|
||
// 优先使用 nickname,其次使用 surname(姓氏)
|
||
const profileNickname = res.data.nickname || res.data.surname || ''
|
||
if (profileNickname) {
|
||
nickname.value = profileNickname
|
||
userStore.updateUserInfo({ nickname: profileNickname })
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户资料失败:', error)
|
||
// API 失败时尝试从 store 获取
|
||
if (userStore.nickname) {
|
||
nickname.value = userStore.nickname
|
||
}
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
getSystemInfo()
|
||
initNickname()
|
||
})
|
||
|
||
return {
|
||
userInfo,
|
||
defaultAvatar,
|
||
statusBarHeight,
|
||
nickname,
|
||
handleBack,
|
||
handleChangeAvatar,
|
||
handleCopyXiangQinNo,
|
||
handleSave,
|
||
initNickname
|
||
}
|
||
},
|
||
// 页面显示时重新初始化
|
||
onShow() {
|
||
if (this.initNickname) {
|
||
this.initNickname()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.personal-page {
|
||
height: 100vh;
|
||
background-color: #f5f6fa;
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 顶部背景图
|
||
.top-bg {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 400rpx;
|
||
z-index: 0;
|
||
|
||
.bg-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
// 自定义导航栏
|
||
.custom-navbar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
|
||
.navbar-content {
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 24rpx;
|
||
|
||
.navbar-back {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.back-icon {
|
||
font-size: 64rpx;
|
||
color: #333;
|
||
font-weight: 300;
|
||
}
|
||
}
|
||
|
||
.navbar-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.navbar-placeholder {
|
||
width: 60rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 头像区域
|
||
.avatar-section {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 60rpx 0;
|
||
|
||
.avatar-wrapper {
|
||
position: relative;
|
||
width: 180rpx;
|
||
height: 180rpx;
|
||
|
||
.avatar-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #87ceeb 0%, #5fb3d4 100%);
|
||
}
|
||
|
||
.avatar-edit-icon {
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 4rpx solid #fff;
|
||
|
||
text {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 表单区域
|
||
.form-section {
|
||
position: relative;
|
||
z-index: 1;
|
||
padding: 0 32rpx;
|
||
|
||
.form-item {
|
||
margin-bottom: 32rpx;
|
||
|
||
.form-label {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.form-input {
|
||
background: #f0f0f0;
|
||
border-radius: 16rpx;
|
||
padding: 28rpx 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.input-field {
|
||
flex: 1;
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
background: transparent;
|
||
}
|
||
|
||
.copy-icon {
|
||
font-size: 26rpx;
|
||
color: #ff9a9a;
|
||
}
|
||
|
||
&.disabled {
|
||
background: #f0f0f0;
|
||
|
||
text {
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 保存按钮
|
||
.btn-section {
|
||
position: relative;
|
||
z-index: 1;
|
||
padding: 60rpx 32rpx;
|
||
|
||
.btn-save {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
line-height: 96rpx;
|
||
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
|
||
border-radius: 48rpx;
|
||
font-size: 34rpx;
|
||
color: #fff;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
</style>
|