496 lines
9.5 KiB
Vue
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>
|