Files
airz-uniapp/packages/account/edit-profile/edit-profile.uvue
T
hujun dbfd637388 feat: 重构评估流程为内存状态管理并接入真实提交与报告接口
本次重构将评估草稿改为内存状态管理,移除本地存储草稿,新增评估流程内存状态模块;接入真实的账户资料获取、头像上传接口,新增账户缓存清理逻辑;重构评估提交逻辑,接入真实提交接口并返回评估ID;新增分析页和报告页的真实状态查询、报告获取功能;更新README文档,记录开发进度和接口状态。
2026-09-24 11:50:07 +08:00

94 lines
6.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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, fetchAccountProfile, saveAccountProfile, uploadAccountAvatar } 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('')
const avatarId = ref('')
let originalName = ''; let originalAvatar = ''; let alive = true
const dirty = (): boolean => nickname.value.trim() != originalName || avatar.value != originalAvatar
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
// 服务端资料优先;请求返回时已开始编辑则不覆盖用户输入。
fetchAccountProfile().then((fresh) => {
if (!alive || dirty()) return
nickname.value = fresh.nickname; avatar.value = fresh.avatar; phone.value = fresh.phone
originalName = fresh.nickname; originalAvatar = fresh.avatar
}).catch((error) => { if (alive) errorMessage.value = error instanceof Error ? error.message : '资料加载失败,请重试' })
})
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 result = await uploadAccountAvatar(path)
if (alive) { avatar.value = result['avatarUrl'] as string; avatarId.value = result['attachmentId'] as string }
}
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 = ''; avatarId.value = 'default'; 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, avatarId.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>