feat(medications): 实现完整的用药管理功能

添加了药物管理的核心功能,包括:
- 药物列表展示和状态管理
- 添加新药物的完整流程
- 服药记录的创建和状态更新
- 药物管理界面,支持激活/停用操作
- Redux状态管理和API服务层
- 相关类型定义和辅助函数

主要文件:
- app/(tabs)/medications.tsx - 主界面,集成Redux数据
- app/medications/add-medication.tsx - 添加药物流程
- app/medications/manage-medications.tsx - 药物管理界面
- store/medicationsSlice.ts - Redux状态管理
- services/medications.ts - API服务层
- types/medication.ts - 类型定义
This commit is contained in:
richarjiang
2025-11-10 10:02:53 +08:00
parent 3aafc50702
commit 25b8e45af8
11 changed files with 3517 additions and 233 deletions

View File

@@ -1,33 +1,94 @@
import { ThemedText } from '@/components/ThemedText';
import { useAppDispatch } from '@/hooks/redux';
import { takeMedicationAction } from '@/store/medicationsSlice';
import type { MedicationDisplayItem } from '@/types/medication';
import { Ionicons } from '@expo/vector-icons';
import dayjs, { Dayjs } from 'dayjs';
import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect';
import { Image } from 'expo-image';
import React from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
export type MedicationStatus = 'upcoming' | 'taken' | 'missed';
export type Medication = {
id: string;
name: string;
dosage: string;
scheduledTime: string;
frequency: string;
status: MedicationStatus;
image: any;
};
import React, { useState } from 'react';
import { Alert, StyleSheet, TouchableOpacity, View } from 'react-native';
export type MedicationCardProps = {
medication: Medication;
medication: MedicationDisplayItem;
colors: (typeof import('@/constants/Colors').Colors)[keyof typeof import('@/constants/Colors').Colors];
selectedDate: Dayjs;
};
export function MedicationCard({ medication, colors, selectedDate }: MedicationCardProps) {
const dispatch = useAppDispatch();
const [isSubmitting, setIsSubmitting] = useState(false);
const scheduledDate = dayjs(`${selectedDate.format('YYYY-MM-DD')} ${medication.scheduledTime}`);
const timeDiffMinutes = scheduledDate.diff(dayjs(), 'minute');
/**
* 处理服药操作
*/
const handleTakeMedication = async () => {
// 检查 recordId 是否存在
if (!medication.recordId || isSubmitting) {
return;
}
// 判断是否早于服药时间1小时以上
if (timeDiffMinutes > 60) {
// 显示二次确认弹窗
Alert.alert(
'尚未到服药时间',
`该用药计划在 ${medication.scheduledTime}现在还早于1小时以上。\n\n是否确认已服用此药物`,
[
{
text: '取消',
style: 'cancel',
onPress: () => {
// 用户取消,不执行任何操作
console.log('用户取消提前服药');
},
},
{
text: '确认已服用',
style: 'default',
onPress: () => {
// 用户确认,执行服药逻辑
executeTakeMedication(medication.recordId!);
},
},
]
);
} else {
// 在正常时间范围内,直接执行服药逻辑
executeTakeMedication(medication.recordId);
}
};
/**
* 执行服药操作(提取公共逻辑)
*/
const executeTakeMedication = async (recordId: string) => {
setIsSubmitting(true);
try {
// 调用 Redux action 标记为已服用
await dispatch(takeMedicationAction({
recordId: recordId,
actualTime: new Date().toISOString(),
})).unwrap();
// 可选:显示成功提示
// Alert.alert('服药成功', '已记录本次服药');
} catch (error) {
console.error('[MEDICATION_CARD] 服药操作失败', error);
Alert.alert(
'操作失败',
error instanceof Error ? error.message : '记录服药时发生错误,请稍后重试',
[{ text: '确定' }]
);
} finally {
setIsSubmitting(false);
}
};
const renderStatusBadge = () => {
if (medication.status === 'missed') {
return (
@@ -104,23 +165,25 @@ export function MedicationCard({ medication, colors, selectedDate }: MedicationC
return (
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
// TODO: 实现服药功能
console.log('服药功能待实现');
}}
onPress={handleTakeMedication}
disabled={isSubmitting}
>
{isLiquidGlassAvailable() ? (
<GlassView
style={[styles.actionButton, styles.actionButtonUpcoming]}
glassEffectStyle="clear"
tintColor="rgba(19, 99, 255, 0.3)"
isInteractive={true}
isInteractive={!isSubmitting}
>
<ThemedText style={styles.actionButtonText}></ThemedText>
<ThemedText style={styles.actionButtonText}>
{isSubmitting ? '提交中...' : '立即服用'}
</ThemedText>
</GlassView>
) : (
<View style={[styles.actionButton, styles.actionButtonUpcoming, styles.fallbackActionButton]}>
<ThemedText style={styles.actionButtonText}></ThemedText>
<ThemedText style={styles.actionButtonText}>
{isSubmitting ? '提交中...' : '立即服用'}
</ThemedText>
</View>
)}
</TouchableOpacity>