feat(medications): 添加药品停用确认弹窗

在药品详情页和管理页面添加停用药品的确认弹窗,防止用户误操作。
当用户尝试停用药品时,会显示确认对话框提醒用户停用后当天已生成的用药计划会被删除且无法恢复。
This commit is contained in:
richarjiang
2025-11-12 10:49:39 +08:00
parent 35f06951a0
commit be55c6f43e
2 changed files with 108 additions and 5 deletions

View File

@@ -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 ? (
<ConfirmationSheet
visible={deactivateSheetVisible}
onClose={() => setDeactivateSheetVisible(false)}
onConfirm={handleDeactivateMedication}
title={`停用 ${medication.name}`}
description="停用后,当天已生成的用药计划会一并删除,且无法恢复。"
confirmText="确认停用"
cancelText="取消"
destructive
loading={deactivateLoading}
/>
) : null}
{/* 图片预览 */}
{medication?.photoUrl && (
<ImageViewing

View File

@@ -1,4 +1,5 @@
import { ThemedText } from '@/components/ThemedText';
import { ConfirmationSheet } from '@/components/ui/ConfirmationSheet';
import { HeaderBar } from '@/components/ui/HeaderBar';
import { IconSymbol } from '@/components/ui/IconSymbol';
import { Colors } from '@/constants/Colors';
@@ -42,7 +43,7 @@ const FORM_LABELS: Record<MedicationForm, string> = {
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<FilterType>('all');
const [pendingMedicationId, setPendingMedicationId] = useState<string | null>(null);
const [deactivateSheetVisible, setDeactivateSheetVisible] = useState(false);
const [medicationToDeactivate, setMedicationToDeactivate] = useState<Medication | null>(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() {
<View style={styles.list}>{filteredMedications.map(renderMedicationCard)}</View>
)}
</ScrollView>
{/* 停用药品确认弹窗 */}
{medicationToDeactivate ? (
<ConfirmationSheet
visible={deactivateSheetVisible}
onClose={() => {
setDeactivateSheetVisible(false);
setMedicationToDeactivate(null);
}}
onConfirm={handleDeactivateMedication}
title={`停用 ${medicationToDeactivate.name}`}
description="停用后,当天已生成的用药计划会一并删除,且无法恢复。"
confirmText="确认停用"
cancelText="取消"
destructive
loading={deactivateLoading}
/>
) : null}
</View>
);
}