Files
zyt/TUICallKit-Vue3/pages/user/userinfo.vue
T
2026-03-18 14:53:09 +08:00

496 lines
9.5 KiB
Vue

<template>
<view class="userinfo-container">
<!-- 顶部标题栏 -->
<!-- 加载状态 -->
<view v-if="loading" class="loading-state">
<text>加载中...</text>
</view>
<!-- 编辑表单 -->
<view v-else class="form-wrapper">
<!-- 头像 -->
<view class="form-section">
<view class="form-group">
<text class="form-label">头像</text>
<view class="avatar-container">
<image
:src="formData.avatar"
class="avatar-image"
mode="aspectFill"
/>
<view class="avatar-upload" @click="uploadAvatar">
<uni-icons type="upload-filled" size="24" color="#FFA500" class="upload-icon"></uni-icons>
<text class="upload-text">更换头像</text>
</view>
</view>
</view>
</view>
<!-- 基本信息 -->
<view class="form-section">
<text class="section-title">基本信息</text>
<view class="form-group">
<text class="form-label">昵称</text>
<input
v-model="formData.nickname"
class="form-input"
placeholder="请输入昵称"
maxlength="50"
/>
</view>
<view class="form-group">
<text class="form-label">性别</text>
<view class="sex-group">
<view
class="sex-btn"
:class="{ active: formData.sex === 1}"
@click="formData.sex = 1"
>
</view>
<view
class="sex-btn"
:class="{ active: formData.sex === 2}"
@click="formData.sex = 2"
>
</view>
</view>
</view>
<view class="form-group">
<text class="form-label">年龄</text>
<input
v-model.number="formData.age"
class="form-input"
type="number"
placeholder="请输入年龄"
min="0"
max="150"
/>
</view>
</view>
<!-- 操作按钮 -->
<view class="button-group">
<button class="btn btn-submit" @click="submitForm" :disabled="submitting">
{{ submitting ? '保存中...' : '保存修改' }}
</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
const instance = getCurrentInstance();
// 2. 从实例中获取全局 $url
const $url = instance.appContext.config.globalProperties.$url;
// 数据
const loading = ref(false)
const submitting = ref(false)
// 表单数据
const formData = ref({
avatar: '',
patient_name: '',
phone: '',
nickname: '',
sex: 1,
age: ''
})
// 页面加载
onMounted(() => {
loadUserInfo()
})
// 加载用户信息
const loadUserInfo = async () => {
loading.value = true
try {
const res = await proxy.apiUrl({
url: '/api/user/info',
method: 'GET',
data: {}
})
if (res.code === 1) {
const userInfo = res.data
formData.value = {
avatar: userInfo.avatar || '',
patient_name: userInfo.patient_name || userInfo.nickname || '',
phone: userInfo.phone || '',
nickname: userInfo.nickname || '',
sex: userInfo.sex =='男'?1:2,
age: userInfo.age || ''
}
} else {
uni.showToast({ title: res.msg || '加载失败', icon: 'none' })
}
} catch (err) {
console.error('加载用户信息失败:', err)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
loading.value = false
}
}
// 上传头像
const uploadAvatar = () => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: async (res) => {
const tempFilePath = res.tempFilePaths[0]
try {
uni.showLoading({ title: '上传中...' })
// 获取token
const token = uni.getStorageSync('token')
// 调用后端上传接口
const uploadRes = await new Promise((resolve, reject) => {
uni.uploadFile({
url: $url+'/api/upload/image',
filePath: tempFilePath,
name: 'file',
header: {
token: token,
'content-type': 'multipart/form-data'
},
success: (res) => {
const data = JSON.parse(res.data)
resolve(data)
},
fail: (err) => {
reject(err)
}
})
})
uni.hideLoading()
if (uploadRes.code === 1) {
// 上传成功,更新头像
formData.value.avatar = uploadRes.data.uri || uploadRes.data
uni.showToast({ title: '头像上传成功', icon: 'success' })
} else {
uni.showToast({ title: uploadRes.msg || '上传失败', icon: 'none' })
}
} catch (err) {
uni.hideLoading()
console.error('上传头像失败:', err)
uni.showToast({ title: '上传失败', icon: 'none' })
}
},
fail: (err) => {
console.error('选择图片失败:', err)
uni.showToast({ title: '选择图片失败', icon: 'none' })
}
})
}
// 验证手机号
const validatePhone = (phone) => {
if (!phone) return true // 可选字段
return /^1[3-9]\d{9}$/.test(phone)
}
// 提交表单
const submitForm = async () => {
// 验证必填字段
if (!formData.value.patient_name) {
uni.showToast({ title: '姓名不能为空', icon: 'none' })
return
}
// 验证手机号格式
if (formData.value.phone && !validatePhone(formData.value.phone)) {
uni.showToast({ title: '手机号格式不正确', icon: 'none' })
return
}
submitting.value = true
try {
// 调用后端API更新用户信息
const res = await proxy.apiUrl({
url: '/api/user/setInfo',
method: 'POST',
data: {
avatar: formData.value.avatar,
phone: formData.value.phone,
nickname: formData.value.nickname,
sex: formData.value.sex,
age: formData.value.age,
patient_name: formData.value.patient_name
}
})
if (res.code === 1) {
// 更新本地存储
const userData = uni.getStorageSync('userData')
const updatedData = {
...userData,
avatar: formData.value.avatar,
phone: formData.value.phone,
nickname: formData.value.nickname,
sex: formData.value.sex,
age: formData.value.age,
patient_name: formData.value.patient_name,
diagnosis: {
...userData.diagnosis,
patient_name: formData.value.patient_name
}
}
uni.setStorageSync('userData', updatedData)
uni.showToast({ title: '保存成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 1500)
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' })
}
} catch (err) {
console.error('保存失败:', err)
uni.showToast({ title: '保存失败', icon: 'none' })
} finally {
submitting.value = false
}
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
</script>
<style scoped>
/* 适老化设计:50-80岁 - 大字号、高对比、大触控区 */
.userinfo-container {
min-height: 100vh;
background: #e8eaed;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
background: #ffffff;
padding: 24rpx 40rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.header-back {
font-size: 48rpx;
color: #FFA500;
width: 88rpx;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
}
.header-title {
font-size: 44rpx;
font-weight: bold;
color: #1a1a1a;
flex: 1;
text-align: center;
}
.header-placeholder {
width: 88rpx;
}
.loading-state {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-size: 36rpx;
color: #555;
}
.form-wrapper {
padding: 40rpx;
}
.form-section {
background: #ffffff;
border-radius: 24rpx;
padding: 40rpx;
margin-bottom: 36rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
}
.section-title {
font-size: 40rpx;
font-weight: bold;
color: #FFA500;
margin-bottom: 32rpx;
display: block;
padding-bottom: 24rpx;
border-bottom: 4rpx solid #f0f0f0;
}
.form-group {
margin-bottom: 36rpx;
}
.form-group:last-child {
margin-bottom: 0;
}
.form-label {
display: block;
font-size: 36rpx;
color: #1a1a1a;
font-weight: 500;
margin-bottom: 20rpx;
line-height: 1.6;
}
.form-input {
width: 100%;
padding: 28rpx;
border: 4rpx solid #e0e0e0;
border-radius: 16rpx;
font-size: 36rpx;
line-height: 1.6;
color: #1a1a1a;
background: #ffffff;
box-sizing: border-box;
display: block;
height: auto;
min-height: 96rpx;
}
.form-input::placeholder {
color: #999;
font-size: 34rpx;
}
.avatar-container {
display: flex;
align-items: center;
gap: 32rpx;
}
.avatar-image {
width: 160rpx;
height: 160rpx;
border-radius: 80rpx;
background: #f0f0f0;
border: 4rpx solid #e0e0e0;
}
.avatar-upload {
flex: 1;
padding: 36rpx;
border: 4rpx dashed #FFA500;
border-radius: 20rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16rpx;
min-height: 120rpx;
}
.upload-icon {
font-size: 56rpx;
}
.upload-text {
font-size: 34rpx;
color: #FFA500;
font-weight: 500;
}
.sex-group {
display: flex;
gap: 28rpx;
}
.sex-btn {
flex: 1;
padding: 32rpx;
border: 4rpx solid #e0e0e0;
border-radius: 16rpx;
text-align: center;
font-size: 38rpx;
line-height: 1.5;
color: #555;
background: #ffffff;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
min-height: 96rpx;
}
.sex-btn.active {
border-color: #FFA500;
background: #fff7e6;
color: #FFA500;
font-weight: bold;
}
.button-group {
display: flex;
gap: 28rpx;
margin-top: 56rpx;
margin-bottom: 56rpx;
}
.btn {
flex: 1;
padding: 36rpx;
border-radius: 20rpx;
font-size: 40rpx;
font-weight: bold;
border: none;
cursor: pointer;
transition: all 0.3s;
min-height: 100rpx;
}
.btn:active {
transform: scale(0.98);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-cancel {
background: #f0f0f0;
color: #555;
}
.btn-submit {
background: #FFA500;
color: #ffffff;
}
.btn-submit:active:not(:disabled) {
background: #e69500;
}
</style>