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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user