base-info-avatar-logic.md 8.0 KB

完善基本信息页面(base-info.vue)头像处理与交互逻辑

概述 ✅

本文件记录 src/pages/patient/profile/infos/base-info.vue 中关于头像(avatar)下载、选择与上传的实现细节,并特别强调你要求的行为:

  • 在页面加载时默认从服务端下载用户头像(不再判断 avatar 地址是否包含特定域名)。
  • 在页面从服务器获取并填充资料期间,禁止用户交互(整体锁定)。
  • 只有当用户主动点击头像控件尝试更改时,才将“需要上传头像”标记设为 true —— 即“只要用户尝试更改就上传”,不要做任何域名判断。

这些变更已在 base-info.vue 中实现,并在此文档中逐条解释。⚠️ 强调:文档中所有代码符号均以 code 标记引用。


关键响应式变量(状态)🔧

  • form:包含 avatar, nickname, phone, age, sex, address
  • regionpicker 的地区数组。
  • loading:当从服务器拉取资料/下载头像时为 true。用于禁用交互并配合 uni.showLoading({ mask: true }) 阻止触摸。见 fetchUserInfo()
  • isChoosing:头像选择防抖/状态。
  • avatarUploading:头像上传中标志。
  • avatarNeedsUpload:当为 true 时,onSubmit() 提交前会触发上传。默认在 fetchUserInfo() 结束时设为 false
  • avatarEditedByUser:表示用户是否主动点击了头像控件(用来控制上传语义)。

页面加载与填充逻辑(fetchUserInfo())📥

  1. 页面显示时触发 onShowfetchUserInfo()
  2. fetchUserInfo() 做以下事:

    • 读取 token(uni.getStorageSync('token')),若无直接返回。
    • set loading = trueuni.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 = trueavatarNeedsUpload.value = true(即只要用户点击尝试更改就上传)。
    • 进入选择流程(防重入控制 isChoosing)。
  • 用户选择成功:onChooseAvatar(e) 会设置 form.value.avatar = url,并再次将 avatarEditedByUseravatarNeedsUpload 设为 true
  • 如果用户取消选择:依然保留 avatarEditedByUser=trueavatarNeedsUpload=true(这是你明确要求的:「只要点击就上传」)。

注:如果你希望“点击但取消后不上传”,可以把 avatarNeedsUpload 的设置移到 onChooseAvatar 成功时再设,而不是 startChooseAvatar。但目前实现为“只要点击就上传”。


上传流程(ensureAvatarUploaded()uploadAvatar())⬆️

  • onSubmit() 检查 avatarNeedsUpload;如果 trueavatarUploading 为 false,会调用 await ensureAvatarUploaded()
  • 旧逻辑会绕过本站域名,但现在已移除域名判断(按你的要求):无论 form.avatar 是哪种 URL,只要 avatarNeedsUpload === true,执行下载再上传流程:
    1. 如果 form.avatar 是远程 URL(isHttpUrl 为 true),先 uni.downloadFile({ url: form.value.avatar }),得到 tempFilePath 后 uploadAvatar(tempFilePath)
    2. 如果 form.avatar 已经是本地临时路径(如拍照/本地选择),直接调用 uploadAvatar(form.value.avatar)
  • uploadAvatar() 使用 uni.uploadFile,解析后端返回的 data.url / data.path / data.fileUrl,如果返回会把 form.value.avatar 更新为服务器的新 URL;若上传成功会将 avatarNeedsUpload.value = false

禁止页面交互(实现)🚫

  • 页面加载并下载服务器资料期间,loading = true
  • 在模板中把不同输入/按钮的 :disabledloading 关联:例如:

    <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
  • 主要 symbol:
    • loading, avatarNeedsUpload, avatarEditedByUser
    • fetchUserInfo()startChooseAvatar()onChooseAvatar()ensureAvatarUploaded()uploadAvatar()

可选优化(未来)🔧

  • 如果你想要更精细的行为(比如点击但取消不上传)我可以把 avatarNeedsUpload 的设置移动到 onChooseAvatar 成功分支。当前实现严格按照你的要求“点击即上传”。
  • 把头像逻辑抽象到 composable(composables/useAvatar.ts),其他 base-info.vue 页面(doctorpatient-family 等)也可以复用相同策略。
  • 增加 UI 提示:当 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.vue
  • src/pages/patient-family/profile/infos/base-info.vue 这样后端不会在 update_user_info 接口中接收 avatar 字段,头像上传仍通过专门接口完成。

这些页面的行为已被统一,详细实现请查看每个页面的 fetchUserInfo()startChooseAvatar()onChooseAvatar()ensureAvatarUploaded() 的代码注释。