Pārlūkot izejas kodu

feat(doctor): 实现患者动态展示功能

- 新增用户活动相关类型定义和API接口
- 实现医生查询绑定患者动态的功能
- 添加患者动态列表的数据转换和格式化逻辑
- 实现动态时间的友好显示(如“10分钟前”)
- 添加患者头像的异步加载和默认头像处理
- 根据活动类型生成对应的中文描述文案
- 替换原有静态模拟数据为真实接口数据
- 完善错误处理和边界情况处理逻辑
mcbaiyun 1 mēnesi atpakaļ
vecāks
revīzija
1172684e2c
2 mainītis faili ar 278 papildinājumiem un 22 dzēšanām
  1. 144 0
      src/api/userActivity.ts
  2. 134 22
      src/pages/doctor/index/index.vue

+ 144 - 0
src/api/userActivity.ts

@@ -0,0 +1,144 @@
+import request from './request'
+
+// 活动类型常量
+export const ACTIVITY_TYPES = {
+  // 血糖数据相关
+  BLOOD_GLUCOSE_UPLOAD: 'BLOOD_GLUCOSE_UPLOAD',
+  BLOOD_GLUCOSE_UPDATE: 'BLOOD_GLUCOSE_UPDATE',
+  BLOOD_GLUCOSE_DELETE: 'BLOOD_GLUCOSE_DELETE',
+
+  // 血压数据相关
+  BLOOD_PRESSURE_UPLOAD: 'BLOOD_PRESSURE_UPLOAD',
+
+  // 心率数据相关
+  HEART_RATE_UPLOAD: 'HEART_RATE_UPLOAD',
+
+  // 体检数据相关
+  PHYSICAL_DATA_UPLOAD: 'PHYSICAL_DATA_UPLOAD',
+
+  // 健康档案相关
+  HEALTH_RECORD_CREATE: 'HEALTH_RECORD_CREATE',
+  HEALTH_RECORD_UPDATE: 'HEALTH_RECORD_UPDATE',
+
+  // 复诊相关
+  FOLLOW_UP_CREATE: 'FOLLOW_UP_CREATE',
+  FOLLOW_UP_UPDATE: 'FOLLOW_UP_UPDATE',
+  FOLLOW_UP_CONFIRM: 'FOLLOW_UP_CONFIRM'
+} as const
+
+// 相关实体类型常量
+export const RELATED_ENTITY_TYPES = {
+  BLOOD_GLUCOSE: 'BLOOD_GLUCOSE',
+  BLOOD_PRESSURE: 'BLOOD_PRESSURE',
+  HEART_RATE: 'HEART_RATE',
+  PHYSICAL_DATA: 'PHYSICAL_DATA',
+  HEALTH_RECORD: 'HEALTH_RECORD',
+  FOLLOW_UP: 'FOLLOW_UP'
+} as const
+
+// 查询患者动态的请求参数接口
+export interface PatientActivityQueryRequest {
+  patientUserId: number // 患者用户ID(医生查询时必填)
+  activityTypes?: string[] // 活动类型列表(可选,用于过滤特定动态)
+  startTime?: string // 开始时间
+  endTime?: string // 结束时间
+  pageNum?: number // 页码,默认1
+  pageSize?: number // 每页大小,默认10,最大100
+}
+
+// 活动响应接口
+export interface UserActivityResponse {
+  id: string // 活动ID
+  userId: number // 操作用户ID
+  activityType: string // 活动类型
+  activityDescription: string // 活动描述
+  relatedEntityType: string // 相关实体类型
+  relatedEntityId?: number // 相关实体ID
+  metadata?: any // 元数据
+  createTime: string // 操作时间
+}
+
+// 分页响应接口
+export interface UserActivityPageResponse {
+  records: UserActivityResponse[] // 数据列表
+  total: number // 总数
+  size: number // 每页大小
+  current: number // 当前页码
+  pages: number // 总页数
+}
+
+// 医生查询患者动态
+export async function queryPatientActivities(params: PatientActivityQueryRequest): Promise<{
+  code: number
+  message: string
+  data: UserActivityPageResponse
+}> {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/user-activity/query-patient-activities',
+    method: 'POST',
+    header: { 'Content-Type': 'application/json' },
+    data: params
+  })
+  return res
+}
+
+// 医生查询所有绑定患者动态
+export async function queryBoundPatientsActivities(params?: Omit<PatientActivityQueryRequest, 'patientUserId'>): Promise<{
+  code: number
+  message: string
+  data: UserActivityPageResponse
+}> {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/user-activity/query-bound-patients-activities',
+    method: 'POST',
+    header: { 'Content-Type': 'application/json' },
+    data: params || {}
+  })
+  return res
+}
+
+/*
+使用示例:
+
+import { queryPatientActivities, queryBoundPatientsActivities, ACTIVITY_TYPES } from '@/api/userActivity'
+
+// 查询患者的所有健康活动
+const result = await queryPatientActivities({
+  patientUserId: 12345,
+  pageNum: 1,
+  pageSize: 10
+})
+
+// 只查询血糖相关活动
+const bloodGlucoseResult = await queryPatientActivities({
+  patientUserId: 12345,
+  activityTypes: [ACTIVITY_TYPES.BLOOD_GLUCOSE_UPLOAD, ACTIVITY_TYPES.BLOOD_GLUCOSE_UPDATE],
+  pageNum: 1,
+  pageSize: 10
+})
+
+// 查询指定时间范围内的活动
+const timeRangeResult = await queryPatientActivities({
+  patientUserId: 12345,
+  startTime: '2025-11-01T00:00:00',
+  endTime: '2025-11-21T23:59:59',
+  pageNum: 1,
+  pageSize: 20
+})
+
+// 医生查询所有绑定患者的动态
+const boundPatientsResult = await queryBoundPatientsActivities({
+  pageNum: 1,
+  pageSize: 10
+})
+
+// 只查询绑定患者的心率和血压数据
+const specificActivitiesResult = await queryBoundPatientsActivities({
+  activityTypes: [ACTIVITY_TYPES.HEART_RATE_UPLOAD, ACTIVITY_TYPES.BLOOD_PRESSURE_UPLOAD],
+  pageNum: 1,
+  pageSize: 20
+})
+
+// 查询所有绑定患者的动态(不传参数,使用默认分页)
+const allActivitiesResult = await queryBoundPatientsActivities()
+*/

+ 134 - 22
src/pages/doctor/index/index.vue

@@ -101,6 +101,7 @@ import TabBar from '@/components/tab-bar.vue'
 import { fetchUserInfo as fetchUserInfoApi } from '@/api/user'
 import { fetchUserInfo as fetchUserInfoApi } from '@/api/user'
 import request from '@/api/request'
 import request from '@/api/request'
 import { handleQrScanResult } from '@/utils/qr'
 import { handleQrScanResult } from '@/utils/qr'
+import { queryBoundPatientsActivities } from '@/api/userActivity'
 
 
 const user = ref<{ avatar?: string; nickname?: string; title?: string }>({})
 const user = ref<{ avatar?: string; nickname?: string; title?: string }>({})
 
 
@@ -126,23 +127,11 @@ const todayReminders = ref({
   abnormalCount: 0
   abnormalCount: 0
 })
 })
 
 
-const patientActivities = ref([
-  {
-    desc: '患者张三血糖数据异常,请关注',
-    time: '10分钟前',
-    patientAvatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
-  },
-  {
-    desc: '患者李四完成了今日复诊',
-    time: '1小时前',
-    patientAvatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
-  },
-  {
-    desc: '患者王五上传了血压数据',
-    time: '2小时前',
-    patientAvatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
-  }
-])
+const patientActivities = ref<Array<{
+  desc: string
+  time: string
+  patientAvatar: string
+}>>([])
 
 
 const loadUser = () => {
 const loadUser = () => {
   try {
   try {
@@ -224,21 +213,94 @@ const fetchTodayReminders = async () => {
 const fetchPatientActivities = async () => {
 const fetchPatientActivities = async () => {
   try {
   try {
     const token = uni.getStorageSync('token')
     const token = uni.getStorageSync('token')
-    if (!token) return
-    const response = await request({
-      url: 'https://wx.baiyun.work/doctor/patient_activities',
-      method: 'GET',
+    if (!token) {
+      console.log('No token found, skipping fetchPatientActivities')
+      return
+    }
+    
+    console.log('Fetching patient activities...')
     
     
+    // 调用真实接口获取患者动态
+    const response = await queryBoundPatientsActivities({
+      pageNum: 1,
+      pageSize: 10
     })
     })
+    
+    console.log('Patient activities response:', response)
+    
     const resp = response.data as any
     const resp = response.data as any
     if (resp && resp.code === 200 && resp.data) {
     if (resp && resp.code === 200 && resp.data) {
-      patientActivities.value = resp.data
+      console.log('Patient activities data:', resp.data)
+      
+      // 转换数据格式,异步获取头像
+      const activitiesPromises = resp.data.records.map(async (activity: any) => ({
+        desc: formatActivityDescription(activity),
+        time: formatTime(activity.createTime),
+        patientAvatar: await getPatientAvatar(activity.userId)
+      }))
+      
+      // 等待所有头像获取完成
+      const activities = await Promise.all(activitiesPromises)
+      console.log('Converted activities:', activities)
+      patientActivities.value = activities
+    } else {
+      console.log('No patient activities data or invalid response')
+      patientActivities.value = []
     }
     }
   } catch (err) {
   } catch (err) {
     console.error('Fetch patient activities error:', err)
     console.error('Fetch patient activities error:', err)
+    // 如果接口调用失败,显示空数据
+    patientActivities.value = []
+  }
+}
+
+// 格式化时间显示
+const formatTime = (createTime: string) => {
+  try {
+    const now = new Date()
+    const create = new Date(createTime)
+    const diff = now.getTime() - create.getTime()
+    
+    const minutes = Math.floor(diff / (1000 * 60))
+    const hours = Math.floor(diff / (1000 * 60 * 60))
+    const days = Math.floor(diff / (1000 * 60 * 60 * 24))
+    
+    if (minutes < 1) return '刚刚'
+    if (minutes < 60) return `${minutes}分钟前`
+    if (hours < 24) return `${hours}小时前`
+    if (days < 7) return `${days}天前`
+    
+    return create.toLocaleDateString('zh-CN')
+  } catch (e) {
+    return createTime
   }
   }
 }
 }
 
 
+// 获取患者头像
+const getPatientAvatar = async (userId: number): Promise<string> => {
+  try {
+    const token = uni.getStorageSync('token')
+    if (!token) return defaultAvatarUrl
+    
+    // 尝试下载用户头像
+    const downloadRes = await uni.downloadFile({
+      url: `https://wx.baiyun.work/user/avatar/${userId}`,
+      header: {
+        Authorization: `Bearer ${token}`
+      }
+    })
+    
+    if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
+      return downloadRes.tempFilePath
+    }
+  } catch (e) {
+    console.error('Download patient avatar error:', e)
+  }
+  
+  // 如果获取失败,使用默认头像
+  return defaultAvatarUrl
+}
+
 // 如果在微信小程序端且未登录,自动跳转到登录页
 // 如果在微信小程序端且未登录,自动跳转到登录页
 onShow(() => {
 onShow(() => {
   const token = uni.getStorageSync('token')
   const token = uni.getStorageSync('token')
@@ -269,6 +331,56 @@ function onItemClick(type: string) {
 function onQrClick() {
 function onQrClick() {
   uni.navigateTo({ url: '/pages/public/profile/qr/index' })
   uni.navigateTo({ url: '/pages/public/profile/qr/index' })
 }
 }
+
+// 格式化活动描述
+const formatActivityDescription = (activity: any) => {
+  // 如果已经有友好的描述,直接返回
+  if (activity.friendlyDescription) {
+    return activity.friendlyDescription
+  }
+  
+  // 根据活动类型生成友好的描述
+  switch (activity.activityType) {
+    case 'BLOOD_GLUCOSE_UPLOAD':
+      return '上传了血糖数据'
+    case 'BLOOD_GLUCOSE_UPDATE':
+      return '更新了血糖数据'
+    case 'BLOOD_PRESSURE_UPLOAD':
+      return '上传了血压数据'
+    case 'HEART_RATE_UPLOAD':
+      return '上传了心率数据'
+    case 'PHYSICAL_DATA_UPLOAD':
+      return '上传了体格数据'
+    case 'HEALTH_RECORD_CREATE':
+      return '创建了健康档案'
+    case 'HEALTH_RECORD_UPDATE':
+      return '更新了健康档案'
+    case 'MEDICATION_CREATE':
+      return '添加了用药记录'
+    case 'MEDICATION_UPDATE':
+      return '更新了用药记录'
+    case 'FOLLOW_UP_CREATE':
+      return '提交了复诊申请'
+    case 'FOLLOW_UP_UPDATE':
+      return '更新了复诊信息'
+    case 'FOLLOW_UP_CONFIRM':
+      return '医生已确认复诊'
+    case 'FOLLOW_UP_CANCEL':
+      return '医生已取消复诊'
+    case 'FOLLOW_UP_COMPLETE':
+      return '医生已完成复诊'
+    case 'USER_BINDING_CREATE':
+      return '绑定了新患者'
+    case 'USER_BINDING_DELETE':
+      return '解除了患者绑定'
+    default:
+      // 如果没有匹配的类型,尝试使用 activityDescription 或返回默认值
+      return activity.activityDescription && !activity.activityDescription.includes('Controller') 
+        ? activity.activityDescription 
+        : '执行了操作'
+  }
+}
+
 </script>
 </script>
 
 
 <style>
 <style>