Prechádzať zdrojové kódy

feat(avatar): 引入头像缓存机制优化加载性能

- 在所有涉及头像下载的页面组件中引入 avatarCache 工具
- 实现头像路径的本地缓存,避免重复下载
- 在获取用户信息时优先检查缓存中的头像路径
- 用户登出或信息更新时清空缓存以确保数据一致性
- 新增 avatarCache.ts 工具模块,提供缓存操作方法
- 修复部分页面跳转路径错误的问题
mcbaiyun 1 mesiac pred
rodič
commit
7c9974fd8c

+ 26 - 11
src/pages/doctor/index/index.vue

@@ -102,6 +102,7 @@ import { fetchUserInfo as fetchUserInfoApi } from '@/api/user'
 import request from '@/api/request'
 import { handleQrScanResult } from '@/utils/qr'
 import { queryBoundPatientsActivities } from '@/api/userActivity'
+import { avatarCache } from '@/utils/avatarCache'
 
 const user = ref<{ avatar?: string; nickname?: string; title?: string }>({})
 
@@ -165,18 +166,25 @@ const fetchUserInfo = async () => {
       if (!resp.data.avatar || !resp.data.avatar.startsWith('http')) {
         const userId = resp.data.id || resp.data.userId
         if (userId) {
-          try {
-            const downloadRes = await uni.downloadFile({
-              url: `https://wx.baiyun.work/user/avatar/${userId}`,
-              header: {
-                Authorization: `Bearer ${token}`
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            resp.data.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await uni.downloadFile({
+                url: `https://wx.baiyun.work/user/avatar/${userId}`,
+                header: {
+                  Authorization: `Bearer ${token}`
+                }
+              })
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                resp.data.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
               }
-            })
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              resp.data.avatar = downloadRes.tempFilePath
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -277,11 +285,16 @@ const formatTime = (createTime: string) => {
 }
 
 // 获取患者头像
-const getPatientAvatar = async (userId: number): Promise<string> => {
+const getPatientAvatar = async (userId: string): Promise<string> => {
   try {
     const token = uni.getStorageSync('token')
     if (!token) return defaultAvatarUrl
     
+    // 检查是否有缓存的头像
+    if (avatarCache.has(userId)) {
+      return avatarCache.get(userId)!
+    }
+    
     // 尝试下载用户头像
     const downloadRes = await uni.downloadFile({
       url: `https://wx.baiyun.work/user/avatar/${userId}`,
@@ -291,6 +304,8 @@ const getPatientAvatar = async (userId: number): Promise<string> => {
     })
     
     if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+      // 缓存头像路径
+      avatarCache.set(userId, downloadRes.tempFilePath)
       return downloadRes.tempFilePath
     }
   } catch (e) {

+ 11 - 3
src/pages/doctor/index/my-patients.vue

@@ -36,6 +36,7 @@ import { onLoad, onShow } from '@dcloudio/uni-app'
 import CustomNav from '@/components/custom-nav.vue'
 import { listUserBindingsByBoundUser, type UserBindingResponse, type UserBindingPageResponse } from '@/api/userBinding'
 import { downloadAvatar } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 interface PatientInfo extends UserBindingResponse {
   avatar?: string
@@ -103,9 +104,16 @@ const fetchPatients = async () => {
       for (const patient of patients.value) {
         try {
           if (patient.patientUserId) {
-            const dlRes: any = await downloadAvatar(String(patient.patientUserId))
-            if (dlRes && dlRes.statusCode === 200 && dlRes.tempFilePath) {
-              patient.avatar = dlRes.tempFilePath
+            // 检查是否有缓存的头像
+            if (avatarCache.has(patient.patientUserId)) {
+              patient.avatar = avatarCache.get(patient.patientUserId)
+            } else {
+              const dlRes: any = await downloadAvatar(String(patient.patientUserId))
+              if (dlRes && dlRes.statusCode === 200 && dlRes.tempFilePath) {
+                patient.avatar = dlRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(patient.patientUserId, dlRes.tempFilePath)
+              }
             }
           }
         } catch (err) {

+ 16 - 6
src/pages/doctor/profile/index.vue

@@ -47,6 +47,7 @@ import CustomNav from '@/components/custom-nav.vue'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi } from '@/api/user'
 import TabBar from '@/components/tab-bar.vue'
 import { isLoggedIn as checkLogin, getRole, logout } from '@/composables/useAuth'
+import { avatarCache } from '@/utils/avatarCache'
 
 const title = ref('个人中心')
 
@@ -105,13 +106,20 @@ const fetchUserInfo = async () => {
       if (!resp.data.avatar || !resp.data.avatar.startsWith('http')) {
         const userId = resp.data.id || resp.data.userId
         if (userId) {
-          try {
-            const downloadRes = await downloadAvatarApi(userId)
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              resp.data.avatar = downloadRes.tempFilePath
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            resp.data.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await downloadAvatarApi(userId)
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                resp.data.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
+              }
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -160,6 +168,8 @@ const onMenuClick = (type: string) => {
 }
 
 const onLogout = async () => {
+  // 清空头像缓存
+  avatarCache.clear()
   await logout({ after: () => { user.value = {} } })
 }
 </script>

+ 17 - 7
src/pages/doctor/profile/infos/base-info.vue

@@ -77,6 +77,7 @@ const loading = ref(false)
 import { onShow } from '@dcloudio/uni-app'
 import CustomNav from '@/components/custom-nav.vue'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi, uploadAvatar as uploadAvatarApi, updateUserInfo as updateUserInfoApi, geocodeNearest as geocodeNearestApi } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 const form = ref({
   avatar: '',
@@ -121,13 +122,20 @@ const fetchUserInfo = async () => {
       {
         const userId = d.id || d.userId
         if (userId) {
-          try {
-            const downloadRes = await downloadAvatarApi(userId)
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              d.avatar = downloadRes.tempFilePath
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            d.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await downloadAvatarApi(userId)
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                d.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
+              }
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -418,8 +426,10 @@ const onSubmit = async () => {
     const resp = response.data as any
     if (resp && resp.code === 200) {
       uni.showToast({ title: '信息更新成功', icon: 'success' })
+      // 清空头像缓存,因为用户信息可能已更改
+      avatarCache.clear()
       setTimeout(() => {
-        uni.switchTab({ url: '/pages/patient/profile/index' })
+        uni.switchTab({ url: '/pages/doctor/profile/index' })
       }, 1500)
     } else {
       throw new Error('Update failed')

+ 18 - 10
src/pages/patient-family/index/index.vue

@@ -76,6 +76,7 @@ import { fetchUserInfo as fetchUserInfoApi } from '@/api/user'
 import TabBar from '@/components/tab-bar.vue'
 import { handleQrScanResult } from '@/utils/qr'
 import request from '@/api/request'
+import { avatarCache } from '@/utils/avatarCache'
 
 const user = ref<{ avatar?: string; nickname?: string }>({})
 
@@ -152,18 +153,25 @@ const fetchUserInfo = async () => {
       if (!resp.data.avatar || !resp.data.avatar.startsWith('http')) {
         const userId = resp.data.id || resp.data.userId
         if (userId) {
-          try {
-            const downloadRes = await uni.downloadFile({
-              url: `https://wx.baiyun.work/user/avatar/${userId}`,
-              header: {
-                Authorization: `Bearer ${token}`
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            resp.data.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await uni.downloadFile({
+                url: `https://wx.baiyun.work/user/avatar/${userId}`,
+                header: {
+                  Authorization: `Bearer ${token}`
+                }
+              })
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                resp.data.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
               }
-            })
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              resp.data.avatar = downloadRes.tempFilePath
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }

+ 11 - 3
src/pages/patient-family/index/my-family.vue

@@ -36,6 +36,7 @@ import { onLoad, onShow } from '@dcloudio/uni-app'
 import CustomNav from '@/components/custom-nav.vue'
 import { listUserBindingsByBoundUser, type UserBindingResponse, type UserBindingPageResponse } from '@/api/userBinding'
 import { downloadAvatar } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 interface FamilyInfo extends UserBindingResponse {
   avatar?: string
@@ -103,9 +104,16 @@ const fetchFamilies = async () => {
       for (const family of families.value) {
         try {
           if (family.patientUserId) {
-            const dlRes: any = await downloadAvatar(String(family.patientUserId))
-            if (dlRes && dlRes.statusCode === 200 && dlRes.tempFilePath) {
-              family.avatar = dlRes.tempFilePath
+            // 检查是否有缓存的头像
+            if (avatarCache.has(family.patientUserId)) {
+              family.avatar = avatarCache.get(family.patientUserId)
+            } else {
+              const dlRes: any = await downloadAvatar(String(family.patientUserId))
+              if (dlRes && dlRes.statusCode === 200 && dlRes.tempFilePath) {
+                family.avatar = dlRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(family.patientUserId, dlRes.tempFilePath)
+              }
             }
           }
         } catch (err) {

+ 20 - 10
src/pages/patient-family/profile/index.vue

@@ -47,6 +47,7 @@ import CustomNav from '@/components/custom-nav.vue'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi } from '@/api/user'
 import TabBar from '@/components/tab-bar.vue'
 import { isLoggedIn as checkLogin, getRole, logout } from '@/composables/useAuth'
+import { avatarCache } from '@/utils/avatarCache'
 
 const title = ref('个人中心')
 
@@ -105,18 +106,25 @@ const fetchUserInfo = async () => {
       if (!resp.data.avatar || !resp.data.avatar.startsWith('http')) {
         const userId = resp.data.id || resp.data.userId
         if (userId) {
-          try {
-            const downloadRes = await uni.downloadFile({
-              url: `https://wx.baiyun.work/user/avatar/${userId}`,
-              header: {
-                Authorization: `Bearer ${token}`
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            resp.data.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await uni.downloadFile({
+                url: `https://wx.baiyun.work/user/avatar/${userId}`,
+                header: {
+                  Authorization: `Bearer ${token}`
+                }
+              })
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                resp.data.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
               }
-            })
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              resp.data.avatar = downloadRes.tempFilePath
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -165,6 +173,8 @@ const onMenuClick = (type: string) => {
 }
 
 const onLogout = async () => {
+  // 清空头像缓存
+  avatarCache.clear()
   await logout({ after: () => { user.value = {} } })
 }
 </script>

+ 17 - 7
src/pages/patient-family/profile/infos/base-info.vue

@@ -77,6 +77,7 @@ const loading = ref(false)
 import { onShow } from '@dcloudio/uni-app'
 import CustomNav from '@/components/custom-nav.vue'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi, uploadAvatar as uploadAvatarApi, updateUserInfo as updateUserInfoApi, geocodeNearest as geocodeNearestApi } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 const form = ref({
   avatar: '',
@@ -121,13 +122,20 @@ const fetchUserInfo = async () => {
       {
         const userId = d.id || d.userId
         if (userId) {
-          try {
-            const downloadRes = await downloadAvatarApi(userId)
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              d.avatar = downloadRes.tempFilePath
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            d.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await downloadAvatarApi(userId)
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                d.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
+              }
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -418,8 +426,10 @@ const onSubmit = async () => {
     const resp = response.data as any
     if (resp && resp.code === 200) {
       uni.showToast({ title: '信息更新成功', icon: 'success' })
+      // 清空头像缓存,因为用户信息可能已更改
+      avatarCache.clear()
       setTimeout(() => {
-        uni.switchTab({ url: '/pages/patient/profile/index' })
+        uni.switchTab({ url: '/pages/patient-family/profile/index' })
       }, 1500)
     } else {
       throw new Error('Update failed')

+ 8 - 0
src/pages/patient/index/index.vue

@@ -95,6 +95,7 @@ import CustomNav from '@/components/custom-nav.vue'
 import TabBar from '@/components/tab-bar.vue'
 import { handleQrScanResult } from '@/utils/qr'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 const user = ref<{ avatar?: string; nickname?: string; age?: number }>({})
 
@@ -172,15 +173,22 @@ const fetchUserInfo = async () => {
       if (!resp.data.avatar || !resp.data.avatar.startsWith('http')) {
         const userId = resp.data.id || resp.data.userId
         if (userId) {
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            resp.data.avatar = avatarCache.get(userId)
+          } else {
             try {
               const downloadRes = await downloadAvatarApi(userId)
               if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
                 resp.data.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
               }
             } catch (e) {
               console.error('Download avatar error:', e)
             }
           }
+        }
       }
       user.value = resp.data
       uni.setStorageSync('user_info', resp.data)

+ 12 - 4
src/pages/patient/index/my-doctor.vue

@@ -72,6 +72,7 @@ import { downloadAvatar } from '@/api/user'
 import { getFollowUpList, updateFollowUp, deleteFollowUp } from '@/api/followUp'
 import type { FollowUp } from '@/api/followUp'
 import { formatDate } from '@/utils/date'
+import { avatarCache } from '@/utils/avatarCache'
 
 // 简化医生信息接口,只包含API实际返回的字段
 interface LocalDoctorInfo {
@@ -130,7 +131,7 @@ const fetchDoctorInfo = async () => {
       uni.showToast({
         title: '未登录',
         icon: 'none'
-      })
+        })
       return
     }
 
@@ -181,9 +182,16 @@ const fetchDoctorInfo = async () => {
         // 尝试下载头像(绑定接口返回的数据中可能没有 avatar)
         try {
           if (boundDoctor.boundUserId) {
-            const dlRes: any = await downloadAvatar(String(boundDoctor.boundUserId))
-            if (dlRes && dlRes.statusCode === 200 && dlRes.tempFilePath) {
-              downloadedAvatar.value = dlRes.tempFilePath
+            // 检查是否有缓存的头像
+            if (avatarCache.has(boundDoctor.boundUserId)) {
+              downloadedAvatar.value = avatarCache.get(boundDoctor.boundUserId) ?? null
+            } else {
+              const dlRes: any = await downloadAvatar(String(boundDoctor.boundUserId))
+              if (dlRes && dlRes.statusCode === 200 && dlRes.tempFilePath) {
+                downloadedAvatar.value = dlRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(boundDoctor.boundUserId, dlRes.tempFilePath)
+              }
             }
           }
         } catch (err) {

+ 16 - 6
src/pages/patient/profile/index.vue

@@ -47,6 +47,7 @@ import CustomNav from '@/components/custom-nav.vue'
 import TabBar from '@/components/tab-bar.vue'
 import { logout } from '@/composables/useAuth'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 const title = ref('个人中心')
 
@@ -105,13 +106,20 @@ const fetchUserInfo = async () => {
       if (!resp.data.avatar || !resp.data.avatar.startsWith('http')) {
         const userId = resp.data.id || resp.data.userId
         if (userId) {
-          try {
-            const downloadRes = await downloadAvatarApi(userId)
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              resp.data.avatar = downloadRes.tempFilePath
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            resp.data.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await downloadAvatarApi(userId)
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                resp.data.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
+              }
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -159,6 +167,8 @@ const onMenuClick = (type: string) => {
 }
 
 const onLogout = async () => {
+  // 清空头像缓存
+  avatarCache.clear()
   await logout({ after: () => { user.value = {} } })
 }
 </script>

+ 20 - 10
src/pages/patient/profile/infos/base-info.vue

@@ -77,6 +77,7 @@ const loading = ref(false)
 import { onShow } from '@dcloudio/uni-app'
 import CustomNav from '@/components/custom-nav.vue'
 import { fetchUserInfo as fetchUserInfoApi, downloadAvatar as downloadAvatarApi, uploadAvatar as uploadAvatarApi, updateUserInfo as updateUserInfoApi, geocodeNearest as geocodeNearestApi } from '@/api/user'
+import { avatarCache } from '@/utils/avatarCache'
 
 const form = ref({
   avatar: '',
@@ -120,18 +121,25 @@ const fetchUserInfo = async () => {
       {
         const userId = d.id || d.userId
         if (userId) {
-          try {
-            const downloadRes = await uni.downloadFile({
-              url: `https://wx.baiyun.work/user/avatar/${userId}`,
-              header: {
-                Authorization: `Bearer ${token}`
+          // 检查是否有缓存的头像
+          if (avatarCache.has(userId)) {
+            d.avatar = avatarCache.get(userId)
+          } else {
+            try {
+              const downloadRes = await uni.downloadFile({
+                url: `https://wx.baiyun.work/user/avatar/${userId}`,
+                header: {
+                  Authorization: `Bearer ${token}`
+                }
+              })
+              if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+                d.avatar = downloadRes.tempFilePath
+                // 缓存头像路径
+                avatarCache.set(userId, downloadRes.tempFilePath)
               }
-            })
-            if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
-              d.avatar = downloadRes.tempFilePath
+            } catch (e) {
+              console.error('Download avatar error:', e)
             }
-          } catch (e) {
-            console.error('Download avatar error:', e)
           }
         }
       }
@@ -423,6 +431,8 @@ const onSubmit = async () => {
     const resp = response.data as any
     if (resp && resp.code === 200) {
       uni.showToast({ title: '信息更新成功', icon: 'success' })
+      // 清空头像缓存,因为用户信息可能已更改
+      avatarCache.clear()
       setTimeout(() => {
         uni.switchTab({ url: '/pages/patient/profile/index' })
       }, 1500)

+ 27 - 0
src/utils/avatarCache.ts

@@ -0,0 +1,27 @@
+// 头像缓存工具模块
+class AvatarCache {
+  private cache: Map<string, string> = new Map()
+
+  // 获取缓存的头像路径
+  get(userId: string): string | undefined {
+    return this.cache.get(userId)
+  }
+
+  // 设置头像路径到缓存
+  set(userId: string, avatarPath: string): void {
+    this.cache.set(userId, avatarPath)
+  }
+
+  // 检查是否存在指定用户ID的缓存
+  has(userId: string): boolean {
+    return this.cache.has(userId)
+  }
+
+  // 清空缓存
+  clear(): void {
+    this.cache.clear()
+  }
+}
+
+// 导出单例实例
+export const avatarCache = new AvatarCache()