Merge remote-tracking branch 'origin/master'

This commit is contained in:
wangxiaowei
2025-12-14 19:29:48 +08:00
3 changed files with 362 additions and 2 deletions

View File

@ -147,6 +147,14 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/my/profile",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/notice/bill",
"type": "page",

View File

@ -123,8 +123,8 @@ onUnload(() => {
const My = {
// 跳转到个人信息
handleToProfile: () => {
if (isLogin.value) {
router.navigateTo('/bundle/profile/profile')
if (!isLogin.value) {
router.navigateTo('/pages/my/profile')
}
else {
router.navigateTo('/pages/login/login')

352
src/pages/my/profile.vue Normal file
View File

@ -0,0 +1,352 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
<script lang="ts" setup>
import type { IUserResult } from '@/api/types/user'
import { updateUserInfo } from '@/api/user'
import { toast } from '@/utils/toast'
import { router } from '@/utils/tools'
import { uploadFileUrl, useUpload } from '@/utils/uploadFile'
import defaultAvatarImg from './img/头像.png'
const OSS = inject('OSS')
const navbarHeight = inject('navbarHeight')
// 本地图片资源
const defaultAvatar = defaultAvatarImg as string
// 用户信息相关
const user = ref<IUserResult>({
id: 0,
sn: 0,
sex: '未知',
account: '',
nickname: '',
real_name: '',
avatar: '',
collect_count: 0,
coupon_count: 0,
create_time: '',
has_auth: false,
has_password: false,
member: 0,
mobile: '',
user_money: '0.00',
version: '',
})
// Action Sheet 相关
const showAvatarActionSheet = ref(false)
const avatarActions = ref([
{
name: '拍照',
value: 'camera',
},
{
name: '从手机相册选择',
value: 'album',
},
])
// 编辑昵称相关
const showEditNicknamePopup = ref(false)
const nicknameInput = ref('')
/**
* 上传头像文件
*/
function handleUploadAvatar(filePath: string) {
const { run: uploadFile } = useUpload<string>(
uploadFileUrl.USER_AVATAR,
{},
{
maxSize: 5,
onSuccess: async (res) => {
try {
const avatarUrl = typeof res === 'string' ? res : (res as any).uri || (res as any).url
await updateUserInfo({ field: 'avatar', value: avatarUrl })
user.value.avatar = avatarUrl
toast.info('头像上传成功')
}
catch (error) {
console.error('更新头像失败:', error)
toast.info('头像上传失败')
}
},
onError: (err) => {
console.error('上传失败:', err)
toast.info('头像上传失败')
},
},
filePath,
)
uploadFile()
}
/**
* 掩码处理手机号
*/
const maskedMobile = computed(() => {
if (!user.value.mobile || user.value.mobile.length !== 11)
return '+86 155****5456'
// 只处理11位手机号
return `+86 ${user.value.mobile.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')}`
})
/**
* 验证昵称是否有效1-10字符一个汉字为一个字符
*/
const isNicknameValid = computed(() => {
const trimmed = nicknameInput.value.trim()
return trimmed.length >= 1 && trimmed.length <= 10
})
const Profile = {
/**
* 初始化用户信息
*/
handleInit: () => {
// getUserInfo().then((res) => {
// user.value = res
// })
},
/**
* 编辑头像 - 显示选择面板
*/
handleEditAvatar: () => {
showAvatarActionSheet.value = true
},
/**
* 处理头像选择
*/
handleSelectAvatarAction: (item: any) => {
showAvatarActionSheet.value = false
if (item.value === 'camera') {
// 拍照
Profile.handleChooseImage(['camera'])
}
else if (item.value === 'album') {
// 从相册选择
Profile.handleChooseImage(['album'])
}
},
/**
* 选择图片
*/
handleChooseImage: (sourceType: ('camera' | 'album')[]) => {
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType,
success: (res) => {
const file = res.tempFiles[0]
if (file) {
handleUploadAvatar(file.tempFilePath)
}
},
fail: (err) => {
console.error('选择图片失败:', err)
if (err.errMsg && !err.errMsg.includes('cancel')) {
toast.info('选择图片失败')
}
},
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType,
success: (res) => {
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
handleUploadAvatar(res.tempFilePaths[0])
}
},
fail: (err) => {
console.error('选择图片失败:', err)
if (err.errMsg && !err.errMsg.includes('cancel')) {
toast.info('选择图片失败')
}
},
})
// #endif
},
/**
* 编辑昵称 - 显示弹窗
*/
handleEditNickname: () => {
nicknameInput.value = user.value.nickname || ''
showEditNicknamePopup.value = true
},
/**
* 关闭编辑昵称弹窗
*/
handleCloseEditNickname: () => {
showEditNicknamePopup.value = false
nicknameInput.value = ''
},
/**
* 保存昵称
*/
handleSaveNickname: async () => {
const trimmed = nicknameInput.value.trim()
if (!trimmed || trimmed.length < 1 || trimmed.length > 10) {
toast.info('昵称限制1-10字符')
return
}
try {
await updateUserInfo({ field: 'nickname', value: trimmed })
user.value.nickname = trimmed
showEditNicknamePopup.value = false
toast.info('昵称修改成功')
}
catch (error) {
console.error('更新昵称失败:', error)
toast.info('昵称修改失败')
}
},
/**
* 修改手机号
*/
handleEditMobile: () => {
router.navigateTo('/pages/login/mobile?type=edit')
},
/**
* 修改密码
*/
handleEditPassword: () => {
// TODO: 实现修改密码功能
console.log('修改密码')
},
/**
* 修改绑定用户
*/
handleEditBoundUser: () => {
// TODO: 实现修改绑定用户功能
console.log('修改绑定用户')
},
}
onLoad(() => {
Profile.handleInit()
})
</script>
<template>
<view>
<!-- 导航栏 -->
<view>
<navbar title="个人信息" custom-class="!bg-white">
<template #right />
</navbar>
</view>
<view class="mx-30rpx mt-20rpx">
<wd-cell-group>
<!-- 头像 -->
<wd-cell is-link title="头像" @click="Profile.handleEditAvatar">
<template #value>
<wd-img width="64rpx" height="64rpx" :src="(user.avatar && typeof user.avatar === 'string' && user.avatar.trim()) ? user.avatar : defaultAvatar" mode="aspectFill" round />
</template>
</wd-cell>
<!-- 昵称 -->
<wd-cell is-link title="昵称" :value="(user.nickname || '昵称名字')" @click="Profile.handleEditNickname" />
<!-- 修改手机号 -->
<wd-cell is-link title="修改手机号" :value="maskedMobile" @click="Profile.handleEditMobile" />
<!-- 修改密码 -->
<wd-cell is-link title="修改密码" @click="Profile.handleEditPassword" />
<!-- 修改绑定用户 -->
<wd-cell is-link title="修改绑定用户" @click="Profile.handleEditBoundUser" />
</wd-cell-group>
</view>
<!-- 头像选择 Action Sheet -->
<wd-action-sheet
v-model="showAvatarActionSheet"
:actions="avatarActions"
cancel-text="取消"
@close="showAvatarActionSheet = false"
@select="Profile.handleSelectAvatarAction"
/>
<!-- 编辑昵称弹窗 -->
<wd-popup
v-model="showEditNicknamePopup"
lock-scroll
custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
position="bottom"
@close="Profile.handleCloseEditNickname"
>
<view class="relative bg-white px-30rpx pb-78rpx pt-50rpx">
<!-- 关闭按钮 -->
<view class="absolute right-30rpx top-18rpx" @click="Profile.handleCloseEditNickname">
<wd-icon name="close" size="20px" color="#C0C4CC" />
</view>
<!-- 标题 -->
<view class="mb-40rpx text-center text-36rpx text-[#121212] leading-50rpx">
修改昵称
</view>
<!-- 输入框 -->
<view class="mb-20rpx">
<view class="mb-20rpx text-30rpx text-[#303133] leading-44rpx">
昵称:
</view>
<wd-input
v-model="nicknameInput"
type="text"
placeholder="请输入昵称"
no-border
custom-class="!bg-[#F6F7F8] !border !border-solid !border-[#EAECF0] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx"
:maxlength="10"
/>
</view>
<!-- 提示文字 -->
<view class="mb-40rpx text-24rpx text-[#606266] leading-34rpx">
昵称限制1-10字符,一个汉字为一个字符
</view>
<!-- 保存按钮 -->
<view
class="h-90rpx rounded-8rpx bg-[#4C9F44] text-center text-[#fff] leading-90rpx"
:class="{ 'opacity-40': !isNicknameValid }"
@click="isNicknameValid && Profile.handleSaveNickname()"
>
保存
</view>
</view>
</wd-popup>
</view>
</template>
<style lang="scss">
page {
background: #f6f7f8;
}
</style>