Files
airz-uniapp/packages/account/edit-profile/edit-profile.uvue
T
hujun 4a69357606 feat: add account profile, search, help and security features
1. add default avatar and multiple notification icons
2. update finance archive note and add form styles
3. add new pages: edit-profile, security, password, change-phone, search, help
4. refactor navigation action layout to support multiple actions
5. add search button to home page
6. add security code component and account service APIs
7. add help and search content services
8. update message notice to use svg icons
9. update asset section in finance assessment
10. update manifest appid and pack options
2026-09-21 15:04:59 +08:00

84 lines
6.0 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, 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>