84 lines
6.0 KiB
Plaintext
84 lines
6.0 KiB
Plaintext
<template>
|
||||
|
|
<view class="page" :style="pageStyle()">
|
|||
|
|
<AppHeader title="修改资料" customBack @back="back" />
|
|||
|
|
<scroll-view scroll-y :style="contentStyle(96, false)"><view class="edit-content" :style="bottomStyle(40)">
|
|||
|
|
<view class="avatar-panel" @click="chooseAvatar"><image class="edit-avatar" :src="avatar.length > 0 ? avatar : DEFAULT_AVATAR" mode="aspectFill" @error="avatar = ''" /><text class="avatar-action">{{ choosing ? '读取中…' : '更换头像' }}</text></view>
|
|||
|
|
<view class="edit-card"><text class="field-title">昵称</text><input v-model="nickname" class="nickname-input" :disabled="saving" maxlength="20" placeholder="请输入昵称" /><text class="field-help">2–20 个字符,用于个人中心展示</text></view>
|
|||
|
|
<view class="phone-row" @click="openPhone"><view class="phone-copy"><text class="field-title">绑定手机号</text><text class="phone-value">{{ maskPhone(phone) }}</text></view><text class="change-label">更换</text><AppIcon name="chevron-right" :size="28" /></view>
|
|||
|
|
<text class="profile-note">昵称与融资档案中的实名信息独立,修改后不影响已填写的融资资料。</text>
|
|||
|
|
<text v-if="errorMessage.length > 0" class="error-text">{{ errorMessage }}</text>
|
|||
|
|
<button class="primary-button save-button" :disabled="saving || choosing" @click="save">{{ saving ? '保存中…' : '保存资料' }}</button>
|
|||
|
|
</view></scroll-view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
<script setup lang="uts">
|
|||
|
|
import AppHeader from '@/components/AppHeader.uvue'
|
|||
|
|
import AppIcon from '@/components/AppIcon.uvue'
|
|||
|
|
import { usePageLayout } from '@/services/layout.uts'
|
|||
|
|
import { getSession } from '@/services/api.uts'
|
|||
|
|
import { DEFAULT_AVATAR, maskPhone, getAccountProfile, saveAccountProfile, readAvatar } from '@/packages/account/services/account.uts'
|
|||
|
|
const { pageStyle, contentStyle, bottomStyle } = usePageLayout()
|
|||
|
|
const nickname = ref(''); const avatar = ref(''); const phone = ref(''); const saving = ref(false); const choosing = ref(false); const errorMessage = ref('')
|
|||
|
|
let originalName = ''; let originalAvatar = ''; let alive = true
|
|||
|
|
onLoad(() => {
|
|||
|
|
if (getSession() == null) { uni.redirectTo({ url: '/packages/account/login/login' }); return }
|
|||
|
|
const profile = getAccountProfile(); nickname.value = profile.nickname; avatar.value = profile.avatar; phone.value = profile.phone
|
|||
|
|
originalName = profile.nickname; originalAvatar = profile.avatar
|
|||
|
|
})
|
|||
|
|
const dirty = (): boolean => nickname.value.trim() != originalName || avatar.value != originalAvatar
|
|||
|
|
const leave = () => { if (getCurrentPages().length > 1) uni.navigateBack(); else uni.redirectTo({ url: '/packages/account/profile/profile' }) }
|
|||
|
|
const back = () => {
|
|||
|
|
if (saving.value) return
|
|||
|
|
if (!dirty()) { leave(); return }
|
|||
|
|
uni.showModal({ title: '放弃修改?', content: '当前修改尚未保存。', confirmText: '放弃修改', success: (result) => { if (result.confirm) { originalName = nickname.value.trim(); originalAvatar = avatar.value; leave() } } })
|
|||
|
|
}
|
|||
|
|
onBackPress((event): boolean => { if (event.from == 'navigateBack') return false; if (saving.value || dirty()) { back(); return true }; return false })
|
|||
|
|
const loadAvatar = async (path: string) => {
|
|||
|
|
choosing.value = true; errorMessage.value = ''
|
|||
|
|
try { const image = await readAvatar(path); if (alive) avatar.value = image }
|
|||
|
|
catch (error) { if (alive) errorMessage.value = error instanceof Error ? error.message : '图片读取失败' }
|
|||
|
|
finally { if (alive) choosing.value = false }
|
|||
|
|
}
|
|||
|
|
const chooseAvatar = () => {
|
|||
|
|
if (saving.value || choosing.value) return
|
|||
|
|
uni.showActionSheet({ itemList: ['从相册选择', '恢复默认头像'], success: (action) => {
|
|||
|
|
if (action.tapIndex == 1) { avatar.value = ''; return }
|
|||
|
|
uni.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album'], success: (result) => {
|
|||
|
|
if (result.tempFilePaths.length == 0) return
|
|||
|
|
if (result.tempFiles[0].size > 512000) { errorMessage.value = '请选择 500KB 以内的图片'; return }
|
|||
|
|
loadAvatar(result.tempFilePaths[0])
|
|||
|
|
}, fail: (error) => { if (!error.errMsg.includes('cancel')) errorMessage.value = '无法打开相册,请检查权限后重试' } })
|
|||
|
|
} })
|
|||
|
|
}
|
|||
|
|
const openPhone = () => { if (!saving.value && !choosing.value) uni.navigateTo({ url: '/packages/account/change-phone/change-phone' }) }
|
|||
|
|
const save = async () => {
|
|||
|
|
if (saving.value || choosing.value) return
|
|||
|
|
errorMessage.value = ''; saving.value = true
|
|||
|
|
try {
|
|||
|
|
if (getAccountProfile().phone != phone.value) throw new Error('登录状态已变化,请重新进入页面')
|
|||
|
|
await saveAccountProfile(nickname.value, avatar.value)
|
|||
|
|
originalName = nickname.value.trim(); originalAvatar = avatar.value
|
|||
|
|
uni.showToast({ title: '资料已保存', icon: 'success' }); leave()
|
|||
|
|
} catch (error) { errorMessage.value = error instanceof Error ? error.message : '保存失败,请稍后重试' }
|
|||
|
|
finally { saving.value = false }
|
|||
|
|
}
|
|||
|
|
onUnload(() => { alive = false })
|
|||
|
|
</script>
|
|||
|
|
<style>
|
|||
|
|
.edit-content { padding: 28rpx 30rpx 40rpx; }
|
|||
|
|
.avatar-panel { align-items: center; padding: 20rpx 0 36rpx; }
|
|||
|
|
.edit-avatar { width: 140rpx; height: 140rpx; border-radius: 70rpx; background-color: #eaf2ff; }
|
|||
|
|
.avatar-action { color: #0868f7; font-size: 27rpx; margin-top: 20rpx; }
|
|||
|
|
.edit-card { padding: 26rpx; border-radius: 18rpx; background-color: #ffffff; }
|
|||
|
|
.field-title { color: #29394f; font-size: 28rpx; font-weight: 600; }
|
|||
|
|
.nickname-input { height: 86rpx; color: #23344e; font-size: 30rpx; border-bottom: 1rpx solid #edf1f6; }
|
|||
|
|
.field-help { color: #8793a6; font-size: 23rpx; margin-top: 16rpx; }
|
|||
|
|
.phone-row { flex-direction: row; align-items: center; padding: 26rpx; margin-top: 20rpx; border-radius: 18rpx; background-color: #ffffff; }
|
|||
|
|
.phone-copy { flex: 1; }
|
|||
|
|
.phone-value { color: #66758a; font-size: 26rpx; margin-top: 12rpx; }
|
|||
|
|
.change-label { color: #0868f7; font-size: 25rpx; margin-right: 8rpx; }
|
|||
|
|
.profile-note { color: #738198; font-size: 24rpx; line-height: 38rpx; margin: 24rpx 4rpx; }
|
|||
|
|
.error-text { color: #d64040; font-size: 25rpx; line-height: 38rpx; margin-bottom: 20rpx; }
|
|||
|
|
.save-button { margin: 12rpx 0 0; }
|
|||
|
|
</style>
|