# 完善基本信息页面(`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`。
- `region`:`picker` 的地区数组。
- `loading`:当从服务器拉取资料/下载头像时为 true。用于禁用交互并配合 `uni.showLoading({ mask: true })` 阻止触摸。见 `fetchUserInfo()`。
- `isChoosing`:头像选择防抖/状态。
- `avatarUploading`:头像上传中标志。
- `avatarNeedsUpload`:当为 `true` 时,`onSubmit()` 提交前会触发上传。默认在 `fetchUserInfo()` 结束时设为 `false`。
- `avatarEditedByUser`:表示用户是否主动点击了头像控件(用来控制上传语义)。
---
## 页面加载与填充逻辑(`fetchUserInfo()`)📥
1. 页面显示时触发 `onShow` → `fetchUserInfo()`。
2. `fetchUserInfo()` 做以下事:
- 读取 token(`uni.getStorageSync('token')`),若无直接返回。
- set `loading = true` 并 `uni.showLoading({ title: '加载中...', mask: true })`,阻止与页面交互。
- 发起 `POST https://wx.baiyun.work/user_info`,解析并填入 `form` 字段。
- 不管 `d.avatar` 是何种 URL,若 `userId` 存在都会调用:
```ts
uni.downloadFile({ url: `https://wx.baiyun.work/user/avatar/${userId}` })
```
若下载成功,`d.avatar = downloadRes.tempFilePath`。
- 填充:`form.value.avatar = d.avatar || form.value.avatar`。
- 默认:
```ts
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`,执行下载再上传流程:
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`。
- 在模板中把不同输入/按钮的 `:disabled` 与 `loading` 关联:例如:
```html
```
- 同时使用 `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` 页面(`doctor`、`patient-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()` 的代码注释。