From be55c6f43e85d90d7eee715797da0ac4727feae6 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Wed, 12 Nov 2025 10:49:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(medications):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=8D=AF=E5=93=81=E5=81=9C=E7=94=A8=E7=A1=AE=E8=AE=A4=E5=BC=B9?= =?UTF-8?q?=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在药品详情页和管理页面添加停用药品的确认弹窗,防止用户误操作。 当用户尝试停用药品时,会显示确认对话框提醒用户停用后当天已生成的用药计划会被删除且无法恢复。 --- app/medications/[medicationId].tsx | 58 ++++++++++++++++++++++++-- app/medications/manage-medications.tsx | 55 +++++++++++++++++++++++- 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/app/medications/[medicationId].tsx b/app/medications/[medicationId].tsx index c51caa5..07d3158 100644 --- a/app/medications/[medicationId].tsx +++ b/app/medications/[medicationId].tsx @@ -82,6 +82,8 @@ export default function MedicationDetailScreen() { const [keyboardHeight, setKeyboardHeight] = useState(0); const [deleteSheetVisible, setDeleteSheetVisible] = useState(false); const [deleteLoading, setDeleteLoading] = useState(false); + const [deactivateSheetVisible, setDeactivateSheetVisible] = useState(false); + const [deactivateLoading, setDeactivateLoading] = useState(false); const [showImagePreview, setShowImagePreview] = useState(false); // 剂量选择相关状态 @@ -341,7 +343,14 @@ export default function MedicationDetailScreen() { const handleToggleMedication = async (nextValue: boolean) => { if (!medication || updatePending) return; - + + // 如果是关闭激活状态,显示确认弹窗 + if (!nextValue) { + setDeactivateSheetVisible(true); + return; + } + + // 如果是开启激活状态,直接执行 try { setUpdatePending(true); const updated = await dispatch( @@ -357,9 +366,6 @@ export default function MedicationDetailScreen() { if (nextValue) { // 如果激活了药品,安排通知 await medicationNotificationService.scheduleMedicationNotifications(updated); - } else { - // 如果停用了药品,取消通知 - await medicationNotificationService.cancelMedicationNotifications(updated.id); } } catch (error) { console.error('[MEDICATION] 处理药品通知失败:', error); @@ -373,6 +379,36 @@ export default function MedicationDetailScreen() { } }; + const handleDeactivateMedication = useCallback(async () => { + if (!medication || deactivateLoading) return; + + try { + setDeactivateLoading(true); + setDeactivateSheetVisible(false); // 立即关闭确认对话框 + + const updated = await dispatch( + updateMedicationAction({ + id: medication.id, + isActive: false, + }) + ).unwrap(); + setMedication(updated); + + // 取消该药品的通知 + try { + await medicationNotificationService.cancelMedicationNotifications(updated.id); + } catch (error) { + console.error('[MEDICATION] 取消药品通知失败:', error); + // 不影响药品状态切换的成功流程,只记录错误 + } + } catch (error) { + console.error('停用药物失败', error); + Alert.alert('操作失败', '停用药物时发生问题,请稍后重试。'); + } finally { + setDeactivateLoading(false); + } + }, [dispatch, medication, deactivateLoading]); + const formLabel = medication ? FORM_LABELS[medication.form] : ''; const dosageLabel = medication ? `${medication.dosageValue} ${medication.dosageUnit}` : '--'; const startDateLabel = medication @@ -1066,6 +1102,20 @@ export default function MedicationDetailScreen() { /> ) : null} + {medication ? ( + setDeactivateSheetVisible(false)} + onConfirm={handleDeactivateMedication} + title={`停用 ${medication.name}?`} + description="停用后,当天已生成的用药计划会一并删除,且无法恢复。" + confirmText="确认停用" + cancelText="取消" + destructive + loading={deactivateLoading} + /> + ) : null} + {/* 图片预览 */} {medication?.photoUrl && ( = { other: '其他', }; -const FILTER_CONFIG: Array<{ key: FilterType; label: string }> = [ +const FILTER_CONFIG: { key: FilterType; label: string }[] = [ { key: 'all', label: '全部' }, { key: 'active', label: '进行中' }, { key: 'inactive', label: '已停用' }, @@ -60,6 +61,9 @@ export default function ManageMedicationsScreen() { const loading = useAppSelector(selectMedicationsLoading); const [activeFilter, setActiveFilter] = useState('all'); const [pendingMedicationId, setPendingMedicationId] = useState(null); + const [deactivateSheetVisible, setDeactivateSheetVisible] = useState(false); + const [medicationToDeactivate, setMedicationToDeactivate] = useState(null); + const [deactivateLoading, setDeactivateLoading] = useState(false); const listLoading = loading.medications && medications.length === 0; @@ -98,6 +102,15 @@ export default function ManageMedicationsScreen() { const handleToggleMedication = useCallback( async (medication: Medication, nextValue: boolean) => { if (pendingMedicationId) return; + + // 如果是关闭激活状态,显示确认弹窗 + if (!nextValue) { + setMedicationToDeactivate(medication); + setDeactivateSheetVisible(true); + return; + } + + // 如果是开启激活状态,直接执行 try { setPendingMedicationId(medication.id); await dispatch( @@ -116,6 +129,28 @@ export default function ManageMedicationsScreen() { [dispatch, pendingMedicationId] ); + const handleDeactivateMedication = useCallback(async () => { + if (!medicationToDeactivate || deactivateLoading) return; + + try { + setDeactivateLoading(true); + setDeactivateSheetVisible(false); // 立即关闭确认对话框 + + await dispatch( + updateMedicationAction({ + id: medicationToDeactivate.id, + isActive: false, + }) + ).unwrap(); + } catch (error) { + console.error('停用药物失败', error); + Alert.alert('操作失败', '停用药物时发生问题,请稍后重试。'); + } finally { + setDeactivateLoading(false); + setMedicationToDeactivate(null); + } + }, [dispatch, medicationToDeactivate, deactivateLoading]); + // 创建独立的药品卡片组件,使用 React.memo 优化渲染 const MedicationCard = React.memo(({ medication, onPress }: { medication: Medication; onPress: () => void }) => { const dosageLabel = `${medication.dosageValue} ${medication.dosageUnit || ''} ${FORM_LABELS[medication.form] ?? ''}`.trim(); @@ -320,6 +355,24 @@ export default function ManageMedicationsScreen() { {filteredMedications.map(renderMedicationCard)} )} + + {/* 停用药品确认弹窗 */} + {medicationToDeactivate ? ( + { + setDeactivateSheetVisible(false); + setMedicationToDeactivate(null); + }} + onConfirm={handleDeactivateMedication} + title={`停用 ${medicationToDeactivate.name}?`} + description="停用后,当天已生成的用药计划会一并删除,且无法恢复。" + confirmText="确认停用" + cancelText="取消" + destructive + loading={deactivateLoading} + /> + ) : null} ); }