staff/pagesB/setting/editInfo.vue

483 lines
9.6 KiB
Vue

<template>
<view class="container">
<!-- 自定义导航栏 -->
<wd-navbar :bordered="false"
custom-style="background: transparent !important; backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(20px) !important;"
safeAreaInsetTop fixed placeholder>
<template #left>
<view class="li-ml-15 li-mt-10 li-flex li-items-center">
<text v-if="hasMultiplePages" class="ri-arrow-left-s-line li-text-70"
@click="toPages({type:'nav'})"></text>
<text v-if="!hasMultiplePages" class="ri-home-5-line li-text-55 li-mb-8 li-mr-10"
@click="toPages({type:'home'})"></text>
<text class="li-text-42">个人信息</text>
</view>
</template>
</wd-navbar>
<!-- 导航栏背景 -->
<view class="nav-bg-layer"></view>
<!-- 页面背景 -->
<view class="page-bg"></view>
<view class="content">
<!-- 个人信息表单 -->
<view class="form-container">
<!-- 头像设置 -->
<view class="avatar-section" @click="chooseAvatar">
<view class="section-left">
<text class="form-label">头像</text>
</view>
<view class="section-right">
<view class="user-avatar">
<image :src="user_info.avatar?user_info.avatar:'/static/m_avatar.png'" mode="aspectFill">
</image>
<!-- <view v-else class="default-avatar">{{ userInfo.realname.substring(0, 1) }}</view> -->
</view>
<text class="ri-arrow-right-s-line arrow-icon"></text>
</view>
</view>
<!-- 昵称设置 -->
<view class="form-section">
<view class="section-left">
<text class="form-label">昵称</text>
</view>
<view class="section-right">
<view class="input-wrapper">
<input class="form-input" type="text" v-model="user_info.realname" placeholder="请输入昵称"
@blur="validateNickname" />
</view>
<text class="ri-arrow-right-s-line arrow-icon"></text>
</view>
</view>
<!-- 手机号展示 -->
<view class="form-section">
<view class="section-left">
<text class="form-label">手机号</text>
</view>
<view class="section-right">
<text class="phone-text">{{ formatPhone(user_info.username) }}</text>
</view>
</view>
</view>
<!-- 保存按钮 -->
<view class="save-btn-container">
<button class="save-btn" @click="saveUserInfo">保存</button>
</view>
</view>
<!-- 头像选择弹窗 -->
<wd-action-sheet v-model="showAvatarPopup" :actions="avatarActions" @select="handleAvatarSelect" />
<wd-toast />
</view>
</template>
<script setup>
import {
ref,
reactive,
onMounted
} from 'vue';
import {
useNavigation
} from '@/hooks/useNavigation';
import {
onLoad
} from '@dcloudio/uni-app'
import {
useToast
} from '@/uni_modules/wot-design-uni';
import {
fileUpload
} from '@/utils/common'
import {
changeInfo,
userInfo
} from '@/api/login';
const Toast = useToast();
// 使用导航 composable
const {
hasMultiplePages, // 是否有多个页面在路由栈中
isTabBarPage, // 当前页面是否为 tabBar 页面
checkRouteStack // 检查当前路由栈状态的方法
} = useNavigation()
// 用户信息
const user_info = ref({})
// 头像选择弹窗
const showAvatarPopup = ref(false);
// 头像选择选项
const avatarActions = [{
name: '从相册选择',
},
{
name: '拍照',
}
];
/**
* 处理头像选择
*/
const handleAvatarSelect = (action) => {
if (action.index === 0) {
handleSelectAlbum();
} else if (action.index === 1) {
handleTakePhoto();
}
};
/**
* 格式化手机号码为带星号格式
*/
const formatPhone = (phone) => {
if (!phone) return '';
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
};
/**
* 校验昵称
*/
const validateNickname = () => {
if (!user_info.value.realname.trim()) {
Toast.error('昵称不能为空')
}
};
/**
* 选择头像
*/
const chooseAvatar = () => {
showAvatarPopup.value = true;
};
/**
* 从相册选择照片
*/
const handleSelectAlbum = () => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album'],
success: async (res) => {
// 选择成功后关闭弹窗
showAvatarPopup.value = false;
// 处理选中的图片
const tempFilePath = res.tempFilePaths[0];
// 上传图片
const fileUrl = await fileUpload(tempFilePath)
Toast.success('上传成功')
user_info.value.avatar = fileUrl
}
});
};
/**
* 拍照
*/
const handleTakePhoto = () => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['camera'],
success: async (res) => {
// 选择成功后关闭弹窗
showAvatarPopup.value = false;
// 处理拍摄的照片
const tempFilePath = res.tempFilePaths[0];
// 上传图片
const fileUrl = await fileUpload(tempFilePath)
Toast.success('上传成功')
user_info.value.avatar = fileUrl
}
});
};
const toPages = (item) => {
if (item.type === 'nav') {
uni.navigateBack()
} else if (item.type === 'home') {
// 这里是项目内部的跳转逻辑
uni.switchTab({
url: '/pages/index/index'
})
}
}
/**
* 保存用户信息
*/
const saveUserInfo = async () => {
// 表单验证
if (!user_info.value.realname.trim()) {
Toast.error('昵称不能为空')
return;
}
// 显示加载中
uni.showLoading({
title: '保存中...'
});
const res = await changeInfo({
real_name: user_info.value.realname,
avatar: user_info.value.avatar
})
uni.hideLoading()
if (res.code == 200) {
Toast.success('保存成功')
setTimeout(() => {
uni.navigateBack();
}, 500)
}
};
const getUserInfo = async () => {
const res = await userInfo()
user_info.value = res.data
}
onMounted(() => {
checkRouteStack()
getUserInfo()
});
</script>
<style lang="scss" scoped>
/* 主容器 */
.container {
min-height: 100vh;
background-color: transparent;
position: relative;
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Segoe UI, Arial, Roboto, PingFang SC, sans-serif;
padding-bottom: env(safe-area-inset-bottom);
}
/* 导航栏背景 */
.nav-bg-layer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: calc(var(--status-bar-height) + 88rpx);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.9) 100%);
z-index: -1;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* 页面背景 */
.page-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #f8fafd 0%, #eef2f9 100%);
z-index: -2;
}
/* 内容区域 */
.content {
position: relative;
z-index: 2;
padding: 30rpx;
}
/* 表单容器 */
.form-container {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
margin-bottom: 40rpx;
}
/* 头像设置区域 */
.avatar-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1px solid #f5f5f5;
.section-left {
.form-label {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
}
.section-right {
display: flex;
align-items: center;
.user-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
overflow: hidden;
margin-right: 16rpx;
border: 2rpx solid rgba(0, 0, 0, 0.05);
image {
width: 100%;
height: 100%;
}
.default-avatar {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #4080FF, #5E96FF);
color: #fff;
font-size: 48rpx;
font-weight: bold;
}
}
.arrow-icon {
font-size: 44rpx;
color: #ccc;
}
}
}
/* 表单项 */
.form-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.section-left {
.form-label {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
}
.section-right {
display: flex;
align-items: center;
.input-wrapper {
text-align: right;
.form-input {
font-size: 30rpx;
color: #333;
text-align: right;
min-width: 200rpx;
}
}
.phone-text {
font-size: 30rpx;
color: #999;
}
.arrow-icon {
font-size: 44rpx;
color: #ccc;
margin-left: 16rpx;
}
}
}
/* 保存按钮 */
.save-btn-container {
padding: 0 20rpx;
margin-top: 60rpx;
.save-btn {
background-color: #4080FF;
color: #fff;
border: none;
height: 96rpx;
line-height: 96rpx;
font-size: 32rpx;
border-radius: 16rpx;
font-weight: 500;
box-shadow: 0 4rpx 16rpx rgba(64, 128, 255, 0.3);
&:active {
background-color: #3a75e6;
}
}
}
/* 头像选择弹窗 */
.avatar-popup {
background-color: #fff;
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
.popup-title {
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: #333;
padding: 30rpx 0;
border-bottom: 1px solid #f0f0f0;
}
.avatar-options {
padding: 20rpx 0;
.option-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30rpx 0;
.option-icon {
font-size: 60rpx;
color: #4080FF;
margin-bottom: 16rpx;
}
.option-text {
font-size: 28rpx;
color: #333;
}
}
}
.cancel-btn {
border-top: 12rpx solid #f5f5f5;
text-align: center;
padding: 30rpx 0;
font-size: 32rpx;
color: #999;
&:active {
background-color: #f8f8f8;
}
}
}
</style>