hookehuyr

fix: 修复更新头像接口参数错误及页面刷新问题

主要修改:
- 修复 updateProfileAPI 参数:使用 avatar_meta_id 替代 avatar_url
- 头像上传后保存 meta_id 字段用于后续更新
- 添加 meta_id 验证,避免传递空值
- 修复"我的"页面:添加 useDidShow 实现返回时自动刷新
- 显示工号(employee_no)替代用户 ID
- 更新项目文档:记录生命周期钩子使用陷阱

技术细节:
- src/pages/avatar/index.vue: 添加 tempAvatarMetaId 存储
- src/pages/mine/index.vue: 使用 useDidShow 而非 useShow
- docs/lessons-learned.md: 新增"生命周期钩子使用陷阱"章节

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
......@@ -91,18 +91,25 @@
| 日期 | 版本 | 变更内容 | 变更原因 | 文档链接 |
|------|------|---------|---------|---------|
| 2026-02-03 | v1.1 | 新增 `employee_no` 字段(工号) | 业务需求变更 | [查看](#) |
| 2026-02-03 | v1.0 | 初始版本 | - | [查看](#) |
**页面调试情况**
| 日期 | 调试页面 | 问题记录 | 解决方案 | 状态 |
|------|---------|---------|---------|------|
| 2026-02-03 | `src/pages/mine/index.vue` | 无 | - | ✅ 已完成 |
| 2026-02-03 | `src/pages/mine/index.vue` | 页面显示 ID 字段,应显示工号 | 将 `userInfo?.id` 改为 `userInfo?.employee_no` | ✅ 已完成 |
**接口状态**: ✅ 已完成
**备注**:
- 返回用户信息:id、avatar_url、name
- **返回用户信息**
- `id` - 用户ID(内部标识)
- `employee_no` - 工号(对外展示)⭐ 新增
- `avatar_url` - 头像URL
- `name` - 姓名
- `avatar_meta_id` - 头像附件ID
- **重要变更**:页面展示工号使用 `employee_no` 字段,而非 `id` 字段
- 在"我的"页面加载时调用
- 401 自动跳转登录页(由 request.js 拦截器处理)
- 实现位置:`src/pages/mine/index.vue:fetchUserProfile()`
......
......@@ -64,15 +64,25 @@ paths:
name:
type: string
title: 姓名
avatar_meta_id:
type: integer
title: 头像的附件ID
employee_no:
type: string
title: 工号
x-apifox-orders:
- id
- name
- avatar_url
- avatar_meta_id
- employee_no
title: 用户信息
required:
- id
- avatar_url
- name
- avatar_meta_id
- employee_no
x-apifox-orders:
- user
required:
......
......@@ -43,14 +43,15 @@ paths:
schema:
type: object
properties:
avatar_url:
type: string
avatar_meta_id:
type: integer
title: 头像的附件ID
required:
- avatar_url
- avatar_meta_id
x-apifox-orders:
- avatar_url
- avatar_meta_id
example:
avatar_url: http://.../new_avatar.jpg
avatar_meta_id: 834933
responses:
'200':
description: ''
......@@ -75,7 +76,7 @@ paths:
x-apifox-ordering: 0
security: []
x-apifox-folder: 用户
x-apifox-status: developing
x-apifox-status: testing
x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-413906669-run
components:
schemas: {}
......
......@@ -6,6 +6,7 @@
- [组件抽取与复用](#组件抽取与复用)
- [NutUI 组件使用陷阱](#nutui-组件使用陷阱)
- [生命周期钩子使用陷阱](#生命周期钩子使用陷阱)
- [静态资源加载问题](#静态资源加载问题)
- [样式处理策略](#样式处理策略)
- [性能优化](#性能优化)
......@@ -237,6 +238,92 @@ setTimeout(() => {
---
## 生命周期钩子使用陷阱
### ❌ 坑: 误用 `useShow` 而非 `useDidShow`(重复 2 次)
**问题描述**:
```javascript
// ❌ 错误:使用了 Vue 3 的 useShow(在 Taro 中不可用)
import { useShow } from '@tarojs/taro'
useShow(() => {
fetchUserProfile()
})
```
**错误表现**:
- IDE 提示 "useShow 未使用"(因为 Taro 中没有这个钩子)
- 页面返回时不会触发刷新
**正确做法**:
```javascript
// ✅ 正确:使用 Taro 的 useDidShow
import Taro, { useLoad, useDidShow } from '@tarojs/taro'
useLoad(() => {
// 页面首次加载时触发
fetchUserProfile()
})
useDidShow(() => {
// 每次页面显示时触发(包括从其他页面返回)
fetchUserProfile()
})
```
**Taro 生命周期钩子对照表**:
| 生命周期 | Taro 钩子 | 用途 | 备注 |
|---------|-----------|------|------|
| 页面加载 | `useLoad` | 首次进入页面时触发 | 只触发一次 |
| 页面显示 | `useDidShow` | 每次页面显示时触发 | 包括从其他页面返回 |
| 页面渲染完成 | `useReady` | 首次渲染完成后触发 | 可操作 DOM |
| 页面隐藏 | `useDidHide` | 页面隐藏时触发 | 清理定时器等 |
| 页面卸载 | `useUnload` | 页面卸载时触发 | 清理资源 |
**⚠️ 重要检查清单(写代码前必须执行)**:
1. **搜索现有用法**: 在项目中搜索关键字,确认其他页面是如何使用的
```bash
# 在终端执行
grep -r "useDidShow\|useShow" src/pages/
```
2. **参考现有页面**: 查看项目中已有的页面实现
```bash
# 例如查看 feedback-list 页面
cat src/pages/feedback-list/index.vue | grep "import.*@tarojs/taro"
```
3. **统一命名规范**: 本项目统一使用 Taro 的钩子
- ✅ `useDidShow`(Taro 官方)
- ❌ `useShow`(Vue 3 Composition API,在小程序中不适用)
**历史记录**:
- 第 1 次错误:在 `src/pages/mine/index.vue` 中使用 `useShow`
- 第 2 次错误:在同一位置再次使用 `useShow`(未检查项目现有用法)
- **教训**: ⚠️ **写代码前必须先搜索项目中是否已有类似实现**
**最佳实践**:
```javascript
// ✅ 推荐的导入方式(一行导入所有需要的钩子)
import Taro, { useLoad, useDidShow, useReady } from '@tarojs/taro'
// ✅ 页面加载时获取数据
useLoad((options) => {
console.log('页面参数:', options)
fetchData()
})
// ✅ 页面显示时刷新数据(从其他页面返回时)
useDidShow(() => {
refreshData()
})
```
---
## 静态资源加载问题
### ❌ 坑: SVG 图标加载失败(500 错误)
......
......@@ -20,6 +20,8 @@ const Api = {
* id: integer; // 用户ID
* avatar_url: string; // 头像
* name: string; // 姓名
* avatar_meta_id: integer; // 头像的附件ID
* employee_no: string; // 工号
* };
* };
* }>}
......@@ -71,7 +73,7 @@ export const logoutAPI = (params) => fn(fetch.post(Api.Logout, params));
* @description 更新个人资料
* @remark
* @param {Object} params 请求参数
* @param {string} params.avatar_url
* @param {integer} params.avatar_meta_id
* @returns {Promise<{
* code: number; // 状态码
* msg: string; // 消息
......
......@@ -46,6 +46,7 @@ import BASE_URL from '@/utils/config'
const go = useGo()
const avatarUrl = ref(defaultAvatar)
const tempAvatarUrl = ref('') // 临时存储上传后的头像URL
const tempAvatarMetaId = ref(null) // 临时存储上传后的头像 meta_id
/**
* @description 更换头像(参考老来赛项目,直接上传到服务器)
......@@ -93,6 +94,9 @@ const onChangeAvatar = () => {
if (data.code === 0) { // 注意:老来赛后端 code=0 表示成功
avatarUrl.value = data.data.src
tempAvatarUrl.value = data.data.src
// 保存 meta_id,用于后续更新头像
tempAvatarMetaId.value = data.data.meta_id || data.data.id || null
console.log('上传成功,meta_id:', tempAvatarMetaId.value)
Taro.showToast({
title: '上传成功',
icon: 'success'
......@@ -136,12 +140,23 @@ const onSave = async () => {
return
}
// 检查是否有 meta_id
if (!tempAvatarMetaId.value) {
console.error('缺少 avatar_meta_id,上传响应可能不包含 meta_id 字段')
Taro.showToast({
title: '上传数据异常,请重试',
icon: 'none'
})
return
}
// 保存到服务器
Taro.showLoading({ title: '保存中...', mask: true })
try {
// ✅ 修复:传递正确的参数 avatar_meta_id
const res = await updateProfileAPI({
avatar_url: tempAvatarUrl.value
avatar_meta_id: tempAvatarMetaId.value
})
Taro.hideLoading()
......
......@@ -18,7 +18,7 @@
<!-- Info -->
<view class="ml-[32rpx] flex-1 flex flex-col justify-center">
<text class="text-[36rpx] font-bold text-gray-800 mb-[8rpx]">{{ userInfo?.name || '加载中...' }}</text>
<text class="text-[28rpx] text-gray-500 mb-[4rpx]">ID: {{ userInfo?.id || '--' }}</text>
<text class="text-[28rpx] text-gray-500 mb-[4rpx]">工号: {{ userInfo?.employee_no || '--' }}</text>
<text class="text-[24rpx] text-gray-400">点击修改头像</text>
</view>
......@@ -75,8 +75,7 @@ import { useUserStore } from '@/stores/user'
import IconFont from '@/components/IconFont.vue'
import TabBar from '@/components/TabBar.vue'
import NavHeader from '@/components/NavHeader.vue'
import Taro from '@tarojs/taro'
import { useLoad } from '@tarojs/taro'
import Taro, { useLoad, useDidShow } from '@tarojs/taro'
import { getProfileAPI } from '@/api/user'
import defaultAvatar from '@/assets/images/icon/avatar.svg'
......@@ -113,12 +112,20 @@ const fetchUserProfile = async () => {
}
/**
* @description 页面加载时获取用户信息
* @description 页面加载时获取用户信息(首次进入)
*/
useLoad(() => {
fetchUserProfile()
})
/**
* @description 页面显示时刷新用户信息(从其他页面返回时)
* @description 例如:从头像设置页面保存后返回,需要刷新显示新头像
*/
useDidShow(() => {
fetchUserProfile()
})
const menuItems = [
{ title: '我的计划书', icon: 'order', path: '/pages/plan/index' },
{ title: '我的收藏', icon: 'star', path: '/pages/favorites/index' },
......