Просмотр исходного кода

feat(patient): 实现患者用药记录管理功能

- 新增用药记录的增删改查接口对接
- 实现用药记录的创建、编辑、删除功能
- 支持根据患者ID获取用药记录列表
- 添加用药记录的状态显示和管理
- 完善用药记录的时间点管理和频次设置
- 优化用药记录的本地存储和用户信息加载
- 修复用药记录ID精度丢失问题
- 添加用药记录操作的错误处理和提示
- 实现用药记录模态框的编辑状态切换
- 完善用药记录的表单验证和数据提交逻辑
mcbaiyun 1 месяц назад
Родитель
Сommit
c11f1c45df
2 измененных файлов с 397 добавлено и 63 удалено
  1. 218 0
      src/api/patientMedication.ts
  2. 179 63
      src/pages/patient/health/medication.vue

+ 218 - 0
src/api/patientMedication.ts

@@ -0,0 +1,218 @@
+import request from './request'
+
+// 用药记录实体
+export interface PatientMedication {
+  id: string // Snowflake 64位ID 使用字符串保存以避免 JS Number 精度丢失
+  patientUserId: number // 患者用户ID
+  medicineName: string // 药品名称
+  dosage: string // 剂量规格
+  frequency: string // 服用频率
+  times: string[] // 服用时间点列表
+  note?: string // 备注信息
+  createTime: string // 创建时间
+  updateTime: string // 更新时间
+}
+
+// 分页查询参数
+export interface MedicationQueryParams {
+  pageNum?: number
+  pageSize?: number
+  startTime?: string
+  endTime?: string
+  patientUserId?: number // 患者用户ID
+  medicineNameKeyword?: string // 药品名称关键字
+}
+
+// 创建用药记录请求参数
+export interface CreateMedicationRequest {
+  patientUserId: number
+  medicineName: string
+  dosage: string
+  frequency: string
+  times: string[]
+  note?: string
+}
+
+// 更新用药记录请求参数
+export interface UpdateMedicationRequest {
+  id: string | number
+  medicineName: string
+  dosage: string
+  frequency: string
+  times: string[]
+  note?: string
+}
+
+// 分页响应
+export interface MedicationPageResponse {
+  records: PatientMedication[]
+  total: number
+  size: number
+  current: number
+  orders: Array<{ column: string; asc: boolean }>
+  optimizeCountSql: boolean
+  searchCount: boolean
+  optimizeJoinOfCountSql: boolean
+  maxLimit: number
+  countId: string
+  pages: number
+}
+
+/**
+ * 创建用药记录
+ * @param payload 创建用药记录请求参数
+ */
+export async function createMedication(payload: CreateMedicationRequest) {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/patient-medication/create',
+    method: 'POST',
+    header: { 'Content-Type': 'application/json' },
+    data: payload
+  })
+  
+  // 强制把 Snowflake ID 字段转换为字符串,避免后续使用 Number 导致精度丢失
+  try {
+    const parsed = res?.data as any
+    if (parsed && parsed.code === 200 && parsed.data) {
+      parsed.data = {
+        ...parsed.data,
+        id: String(parsed.data.id)
+      }
+      res.data = parsed
+    }
+  } catch (e) {
+    // ignore
+  }
+  
+  return res
+}
+
+/**
+ * 更新用药记录
+ * @param id 用药记录ID
+ * @param payload 更新用药记录请求参数
+ */
+export async function updateMedication(id: string | number, payload: UpdateMedicationRequest) {
+  const res: any = await request({
+    url: `https://wx.baiyun.work/patient-medication/${encodeURIComponent(String(id))}`,
+    method: 'PUT',
+    header: { 'Content-Type': 'application/json' },
+    data: payload
+  })
+  
+  // 强制把 Snowflake ID 字段转换为字符串,避免后续使用 Number 导致精度丢失
+  try {
+    const parsed = res?.data as any
+    if (parsed && parsed.code === 200 && parsed.data) {
+      parsed.data = {
+        ...parsed.data,
+        id: String(parsed.data.id)
+      }
+      res.data = parsed
+    }
+  } catch (e) {
+    // ignore
+  }
+  
+  return res
+}
+
+/**
+ * 分页查询用药记录
+ * @param query 查询参数
+ */
+export async function getMedicationList(query: MedicationQueryParams) {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/patient-medication/list',
+    method: 'POST',
+    header: { 'Content-Type': 'application/json' },
+    data: query
+  })
+  
+  // 强制把 Snowflake ID 字段转换为字符串,避免后续使用 Number 导致精度丢失
+  try {
+    const parsed = res?.data as any
+    if (parsed && parsed.code === 200 && parsed.data && Array.isArray(parsed.data.records)) {
+      parsed.data.records = parsed.data.records.map((r: any) => ({
+        ...r,
+        id: String(r.id)
+      }))
+      res.data = parsed
+    }
+  } catch (e) {
+    // ignore
+  }
+  
+  return res
+}
+
+/**
+ * 删除用药记录
+ * @param id 用药记录ID
+ */
+export async function deleteMedication(id: string | number) {
+  const res: any = await request({
+    url: `https://wx.baiyun.work/patient-medication/${encodeURIComponent(String(id))}`,
+    method: 'DELETE'
+  })
+  return res
+}
+
+/**
+ * 获取用药记录详情
+ * @param id 用药记录ID
+ */
+export async function getMedicationById(id: string | number) {
+  const res: any = await request({
+    url: `https://wx.baiyun.work/patient-medication/${encodeURIComponent(String(id))}`,
+    method: 'GET'
+  })
+  
+  // 强制把 Snowflake ID 字段转换为字符串,避免后续使用 Number 导致精度丢失
+  try {
+    const parsed = res?.data as any
+    if (parsed && parsed.code === 200 && parsed.data) {
+      parsed.data = {
+        ...parsed.data,
+        id: String(parsed.data.id)
+      }
+      res.data = parsed
+    }
+  } catch (e) {
+    // ignore
+  }
+  
+  return res
+}
+
+/**
+ * 根据患者ID获取用药记录列表
+ * @param patientUserId 患者用户ID
+ */
+export async function getMedicationListByPatientId(patientUserId: number) {
+  // 检查参数
+  if (!patientUserId) {
+    return Promise.reject(new Error('Patient user ID is required'))
+  }
+  
+  const res: any = await request({
+    url: `https://wx.baiyun.work/patient-medication/patient/${encodeURIComponent(String(patientUserId))}`,
+    method: 'GET'
+  })
+  
+  // 强制把 Snowflake ID 字段转换为字符串,避免后续使用 Number 导致精度丢失
+  try {
+    const parsed = res?.data as any
+    if (parsed && parsed.code === 200 && parsed.data && Array.isArray(parsed.data)) {
+      parsed.data = parsed.data.map((r: any) => ({
+        ...r,
+        id: String(r.id)
+      }))
+      res.data = parsed
+    }
+  } catch (e) {
+    // ignore
+  }
+  
+  return res
+}

+ 179 - 63
src/pages/patient/health/medication.vue

@@ -34,7 +34,7 @@
 
         <view class="card-footer">
           <button class="action-btn edit" @click="editMedication(medication.id)">编辑</button>
-          <button class="action-btn delete" @click="deleteMedication(medication.id)">删除</button>
+          <button class="action-btn delete" @click="deleteMedicationRecord(medication.id)">删除</button>
         </view>
       </view>
     </view>
@@ -61,7 +61,7 @@
       <view class="modal-panel add-medication-modal">
         <view class="drag-handle"></view>
         <view class="modal-header">
-          <text class="modal-title">添加用药</text>
+          <text class="modal-title">{{ isEditing ? '编辑用药' : '添加用药' }}</text>
         </view>
 
         <view class="modal-inner">
@@ -121,7 +121,7 @@
 
         <view class="modal-footer">
           <button class="btn-secondary" @click="closeAdd">取消</button>
-          <button class="btn-primary" @click="confirmAdd">保存</button>
+          <button class="btn-primary" @click="confirmAdd">{{ isEditing ? '保存' : '添加' }}</button>
         </view>
       </view>
     </view>
@@ -193,6 +193,8 @@
 import { ref, computed, onMounted, watch } from 'vue'
 import CustomNav from '@/components/custom-nav.vue'
 import { getMedicineList } from '@/api/medicine'
+import { getMedicationListByPatientId, createMedication, deleteMedication, updateMedication } from '@/api/patientMedication'
+import { onShow } from '@dcloudio/uni-app'
 
 console.log('Medication component loaded')
 
@@ -203,6 +205,7 @@ interface Medication {
   frequency: string
   time: string
   note?: string
+  status?: string
 }
 
 interface Medicine {
@@ -211,28 +214,36 @@ interface Medicine {
   pinyinFirstLetters: string
 }
 
-const medications = ref<Medication[]>([
-  {
-    id: '1',
-    name: '阿司匹林',
-    dosage: '100mg',
-    frequency: '每日1次,每次1粒',
-    time: '08:00',
-    note: '饭后服用'
-  },
-  {
-    id: '2',
-    name: '硝苯地平',
-    dosage: '10mg',
-    frequency: '每日3次,每次1粒',
-    time: '07:00, 15:00, 21:00'
+// 用户信息
+const user = ref<{ id?: string; nickname?: string; role?: string | number }>({})
+
+// 从本地存储加载用户信息
+const loadUser = () => {
+  try {
+    const u = uni.getStorageSync('user_info')
+    console.log('Loaded user_info from storage:', u)
+    if (u) {
+      user.value = u
+      console.log('User data set:', user.value)
+    }
+  } catch (e) {
+    console.error('Error loading user info:', e)
   }
-])
+}
+
+// 获取当前用户ID
+const currentUserId = computed(() => user.value.id ? Number(user.value.id) : undefined)
+
+const medications = ref<Medication[]>([])
 
 // 模态框控制
 const showAdd = ref(false)
 const showMedicinePicker = ref(false)
 
+// 编辑状态
+const isEditing = ref(false)
+const editingId = ref<string>('')
+
 // 添加用药表单数据
 const selectedMedicine = ref<Medicine | null>(null)
 const addDosage = ref('')
@@ -273,13 +284,13 @@ const filteredMedicines = computed(() => {
 })
 
 // 获取状态文本
-// const getStatusText = (status: string) => {
-//   switch (status) {
-//     case 'active': return '进行中'
-//     case 'inactive': return '已停用'
-//     default: return status
-//   }
-// }
+const getStatusText = (status: string) => {
+  switch (status) {
+    case 'active': return '进行中'
+    case 'inactive': return '已停用'
+    default: return status
+  }
+}
 
 // 打开添加模态框
 const openAdd = () => {
@@ -334,6 +345,9 @@ const closeAdd = () => {
   eachTime.value = 1
   addTimes.value = []
   addNote.value = ''
+  // 重置编辑状态
+  isEditing.value = false
+  editingId.value = ''
 }
 
 // 打开药品选择器
@@ -407,8 +421,18 @@ const selectMedicine = (medicine: Medicine) => {
   closeMedicinePicker()
 }
 
-// 确认添加用药
-const confirmAdd = () => {
+// 确认添加/编辑用药
+const confirmAdd = async () => {
+  // 检查用户ID是否存在
+  if (!currentUserId.value) {
+    console.error('Cannot create medication: User ID not found')
+    uni.showToast({
+      title: '用户信息异常',
+      icon: 'none'
+    })
+    return
+  }
+  
   if (!selectedMedicine.value) {
     uni.showToast({
       title: '请选择药品',
@@ -436,22 +460,54 @@ const confirmAdd = () => {
   const frequency = `每日${dailyTimes.value}次,每次${eachTime.value}粒`
   const timeStr = addTimes.value.join(', ')
 
-  const newMedication: Medication = {
-    id: Date.now().toString(),
-    name: selectedMedicine.value.name,
-    dosage: addDosage.value.trim(),
-    frequency: frequency,
-    time: timeStr,
-    note: addNote.value.trim() || undefined
+  try {
+    let result: any
+    if (isEditing.value) {
+      // 编辑用药
+      const payload = {
+        id: editingId.value,
+        medicineName: selectedMedicine.value.name,
+        dosage: addDosage.value.trim(),
+        frequency: frequency,
+        times: addTimes.value,
+        note: addNote.value.trim() || undefined
+      }
+      result = await updateMedication(editingId.value, payload)
+    } else {
+      // 添加用药
+      const payload = {
+        patientUserId: currentUserId.value!,
+        medicineName: selectedMedicine.value.name,
+        dosage: addDosage.value.trim(),
+        frequency: frequency,
+        times: addTimes.value,
+        note: addNote.value.trim() || undefined
+      }
+      result = await createMedication(payload)
+    }
+    
+    if (result?.data?.code === 200) {
+      uni.showToast({
+        title: isEditing.value ? '编辑成功' : '添加成功',
+        icon: 'success'
+      })
+      
+      // 重新加载用药列表
+      loadMedications()
+      closeAdd()
+    } else {
+      uni.showToast({
+        title: isEditing.value ? '编辑失败' : '添加失败',
+        icon: 'none'
+      })
+    }
+  } catch (error) {
+    console.error(isEditing.value ? 'Edit medication error:' : 'Add medication error:', error)
+    uni.showToast({
+      title: isEditing.value ? '编辑失败' : '添加失败',
+      icon: 'none'
+    })
   }
-
-  medications.value.unshift(newMedication)
-  uni.showToast({
-    title: '添加成功',
-    icon: 'success'
-  })
-
-  closeAdd()
 }
 
 // 扫码录入药品
@@ -466,6 +522,10 @@ const scanMedication = () => {
 const editMedication = (id: string) => {
   const medication = medications.value.find(m => m.id === id)
   if (medication) {
+    // 设置编辑状态
+    isEditing.value = true
+    editingId.value = id
+    
     // 加载选中药物的信息到编辑表单
     selectedMedicine.value = {
       id: '',
@@ -494,22 +554,80 @@ const editMedication = (id: string) => {
 }
 
 // 删除用药
-const deleteMedication = (id: string) => {
+const deleteMedicationRecord = (id: string) => {
   uni.showModal({
     title: '确认删除',
     content: '确定要删除这个用药记录吗?',
-    success: (res) => {
+    success: async (res) => {
       if (res.confirm) {
-        const index = medications.value.findIndex(m => m.id === id)
-        if (index !== -1) {
-          medications.value.splice(index, 1)
-          uni.showToast({ title: '删除成功', icon: 'success' })
+        try {
+          const result: any = await deleteMedication(id)
+          
+          if (result?.data?.code === 200) {
+            uni.showToast({
+              title: '删除成功',
+              icon: 'success'
+            })
+            
+            // 重新加载用药列表
+            loadMedications()
+          } else {
+            uni.showToast({
+              title: '删除失败',
+              icon: 'none'
+            })
+          }
+        } catch (error) {
+          console.error('Delete medication error:', error)
+          uni.showToast({
+            title: '删除失败',
+            icon: 'none'
+          })
         }
       }
     }
   })
 }
 
+// 加载用药列表
+const loadMedications = async () => {
+  // 检查用户ID是否存在
+  if (!currentUserId.value) {
+    console.error('Cannot load medications: User ID not found')
+    uni.showToast({
+      title: '用户信息异常',
+      icon: 'none'
+    })
+    return
+  }
+  
+  try {
+    const result: any = await getMedicationListByPatientId(currentUserId.value!)
+    
+    if (result?.data?.code === 200) {
+      medications.value = result.data.data.map((item: any) => ({
+        id: item.id,
+        name: item.medicineName,
+        dosage: item.dosage,
+        frequency: item.frequency,
+        time: item.times ? item.times.join(', ') : '',
+        note: item.note
+      }))
+    } else {
+      uni.showToast({
+        title: '获取用药记录失败',
+        icon: 'none'
+      })
+    }
+  } catch (error) {
+    console.error('Load medications error:', error)
+    uni.showToast({
+      title: '获取用药记录失败',
+      icon: 'none'
+    })
+  }
+}
+
 // 监听每日次数变化,自动设置预设时间
 watch(dailyTimes, (newVal) => {
   if (presetTimes[newVal]) {
@@ -521,7 +639,18 @@ watch(dailyTimes, (newVal) => {
 })
 
 onMounted(() => {
-  // 这里可以调用API获取用药数据
+  // 组件挂载时的初始化逻辑(如果需要)
+})
+
+onShow(() => {
+  // 检查登录态
+  const token = uni.getStorageSync('token')
+  if (!token) {
+    uni.reLaunch({ url: '/pages/public/login/index' })
+    return
+  }
+  loadUser()
+  loadMedications()
 })
 </script>
 
@@ -590,20 +719,7 @@ onMounted(() => {
   color: #999;
 }
 
-.status-badge {
-  padding: 8rpx 16rpx;
-  border-radius: 10rpx;
-  font-size: 24rpx;
-  color: #fff;
-}
-
-.status-badge.active {
-  background-color: #2ed573;
-}
 
-.status-badge.inactive {
-  background-color: #ccc;
-}
 
 .card-body {
   padding: 30rpx;