base-info.vue)头像处理与交互逻辑本文件记录 src/pages/patient/profile/infos/base-info.vue 中关于头像(avatar)下载、选择与上传的实现细节,并特别强调你要求的行为:
这些变更已在 base-info.vue 中实现,并在此文档中逐条解释。⚠️ 强调:文档中所有代码符号均以 code 标记引用。
form:包含 avatar, nickname, phone, age, sex, address。region:picker 的地区数组。loading:当从服务器拉取资料/下载头像时为 true。用于禁用交互并配合 uni.showLoading({ mask: true }) 阻止触摸。见 fetchUserInfo()。isChoosing:头像选择防抖/状态。avatarUploading:头像上传中标志。avatarNeedsUpload:当为 true 时,onSubmit() 提交前会触发上传。默认在 fetchUserInfo() 结束时设为 false。avatarEditedByUser:表示用户是否主动点击了头像控件(用来控制上传语义)。fetchUserInfo())📥onShow → fetchUserInfo()。fetchUserInfo() 做以下事:
uni.getStorageSync('token')),若无直接返回。loading = true 并 uni.showLoading({ title: '加载中...', mask: true }),阻止与页面交互。POST https://wx.baiyun.work/user_info,解析并填入 form 字段。不管 d.avatar 是何种 URL,若 userId 存在都会调用:
uni.downloadFile({ url: `https://wx.baiyun.work/user/avatar/${userId}` })
若下载成功,d.avatar = downloadRes.tempFilePath。
填充:form.value.avatar = d.avatar || form.value.avatar。
默认:
avatarNeedsUpload.value = false
avatarEditedByUser.value = false
即:完成从服务器获取资料后默认不上传,等用户主动更改。
finally 中 uni.hideLoading() 和 loading = false,恢复交互。
重要:这里的实现逻辑不会对
d.avatar的 URL 做域名判断或条件分支;总是尝试下载(只要有 userId)。
startChooseAvatar / onChooseAvatar)🖱️startChooseAvatar() 会先检查 loading,若正在下载则忽略点击;否则:avatarEditedByUser.value = true 与 avatarNeedsUpload.value = true(即只要用户点击尝试更改就上传)。isChoosing)。onChooseAvatar(e) 会设置 form.value.avatar = url,并再次将 avatarEditedByUser 与 avatarNeedsUpload 设为 true。avatarEditedByUser=true 与 avatarNeedsUpload=true(这是你明确要求的:「只要点击就上传」)。注:如果你希望“点击但取消后不上传”,可以把
avatarNeedsUpload的设置移到onChooseAvatar成功时再设,而不是startChooseAvatar。但目前实现为“只要点击就上传”。
ensureAvatarUploaded() 与 uploadAvatar())⬆️onSubmit() 检查 avatarNeedsUpload;如果 true 且 avatarUploading 为 false,会调用 await ensureAvatarUploaded()。form.avatar 是哪种 URL,只要 avatarNeedsUpload === true,执行下载再上传流程:
form.avatar 是远程 URL(isHttpUrl 为 true),先 uni.downloadFile({ url: form.value.avatar }),得到 tempFilePath 后 uploadAvatar(tempFilePath)。form.avatar 已经是本地临时路径(如拍照/本地选择),直接调用 uploadAvatar(form.value.avatar)。uploadAvatar() 使用 uni.uploadFile,解析后端返回的 data.url / data.path / data.fileUrl,如果返回会把 form.value.avatar 更新为服务器的新 URL;若上传成功会将 avatarNeedsUpload.value = false。loading = true。在模板中把不同输入/按钮的 :disabled 与 loading 关联:例如:
<input :disabled="loading" ... />
<button :disabled="loading || isChoosing || avatarUploading" ... />
<picker :disabled="loading" ... />
同时使用 uni.showLoading({ mask:true }) 让小程序层面遮罩并拦截触摸操作,避免 race 条件。
fetchUserInfo() 中只要 userId 存在就 uni.downloadFile 下载头像(不做 http/域名判断)。startChooseAvatar() 中设置 avatarNeedsUpload = true(所以只要用户点了头像就会在提交时上传)。loading + uni.showLoading({ mask:true }),并在 template 将 disabled 关联到 loading。loading 时界面不可操作,头像能从 user/avatar/${userId} 下载并展示。onSubmit 触发时进行头像 upload(因为点击就 set avatarNeedsUpload=true)。ensureAvatarUploaded() 下载或上传失败,应在 catch 里提示并保持页面可用(当前实现就是此行为)。src/pages/patient/profile/infos/base-info.vue。loading, avatarNeedsUpload, avatarEditedByUser。fetchUserInfo()、startChooseAvatar()、onChooseAvatar()、ensureAvatarUploaded()、uploadAvatar()。avatarNeedsUpload 的设置移动到 onChooseAvatar 成功分支。当前实现严格按照你的要求“点击即上传”。composables/useAvatar.ts),其他 base-info.vue 页面(doctor、patient-family 等)也可以复用相同策略。avatarEditedByUser 为 true 时,在界面上显示提示信息 "你已更改头像,提交后会上传并替换服务器头像"。文档到此结束!如需我把这份文档复制并同步到其它 base-info.vue(doctor / patient-family)或把 avatar 抽象为 composable 可复用模块,我可以继续实现。
src/pages/patient/profile/infos/base-info.vue (原始实现)src/pages/doctor/profile/infos/base-info.vue(已同步:相同的 avatar 下载、禁止交互、点击即上传 逻辑)src/pages/patient-family/profile/infos/base-info.vue(已同步:相同的 avatar 下载、禁止交互、点击即上传 逻辑)另外,已经同步 update_user_info payload 行为(删除 avatar 字段)到:
src/pages/doctor/profile/infos/base-info.vuesrc/pages/patient-family/profile/infos/base-info.vue
这样后端不会在 update_user_info 接口中接收 avatar 字段,头像上传仍通过专门接口完成。这些页面的行为已被统一,详细实现请查看每个页面的 fetchUserInfo()、startChooseAvatar()、onChooseAvatar()、ensureAvatarUploaded() 的代码注释。