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
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user