import { Ionicons } from '@expo/vector-icons'; import * as Haptics from 'expo-haptics'; import React, { useEffect, useRef } from 'react'; import { Animated, Dimensions, Modal, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; const { width: screenWidth } = Dimensions.get('window'); interface ConfirmDialogProps { visible: boolean; onClose: () => void; title: string; message?: string; confirmText?: string; cancelText?: string; onConfirm: () => void; destructive?: boolean; icon?: keyof typeof Ionicons.glyphMap; iconColor?: string; } export function ConfirmDialog({ visible, onClose, title, message, confirmText = '确定', cancelText = '取消', onConfirm, destructive = false, icon, iconColor, }: ConfirmDialogProps) { const scaleAnim = useRef(new Animated.Value(0.8)).current; const opacityAnim = useRef(new Animated.Value(0)).current; useEffect(() => { if (visible) { // 显示动画 Animated.parallel([ Animated.spring(scaleAnim, { toValue: 1, useNativeDriver: true, tension: 100, friction: 8, }), Animated.timing(opacityAnim, { toValue: 1, duration: 200, useNativeDriver: true, }), ]).start(); } else { // 隐藏动画 Animated.parallel([ Animated.timing(scaleAnim, { toValue: 0.8, duration: 150, useNativeDriver: true, }), Animated.timing(opacityAnim, { toValue: 0, duration: 150, useNativeDriver: true, }), ]).start(); } }, [visible]); const handleConfirm = () => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); onClose(); // 延迟执行确认回调,让关闭动画先完成 setTimeout(() => { onConfirm(); }, 100); }; const handleCancel = () => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); onClose(); }; if (!visible) return null; const defaultIconColor = destructive ? '#EF4444' : '#3B82F6'; const confirmButtonColor = destructive ? '#EF4444' : '#3B82F6'; return ( {/* 背景遮罩 */} {/* 弹窗内容 */} {/* 图标 */} {icon && ( )} {/* 标题 */} {title} {/* 消息 */} {message && {message}} {/* 按钮组 */} {cancelText} {confirmText} ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 40, }, backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0, 0, 0, 0.4)', }, dialog: { backgroundColor: '#FFFFFF', borderRadius: 20, paddingTop: 32, paddingBottom: 24, paddingHorizontal: 24, width: '100%', maxWidth: screenWidth - 80, alignItems: 'center', shadowColor: '#000', shadowOpacity: 0.15, shadowRadius: 20, shadowOffset: { width: 0, height: 10 }, elevation: 10, }, iconContainer: { width: 64, height: 64, borderRadius: 32, alignItems: 'center', justifyContent: 'center', marginBottom: 20, }, title: { fontSize: 18, fontWeight: '700', color: '#111827', textAlign: 'center', marginBottom: 8, }, message: { fontSize: 15, color: '#6B7280', textAlign: 'center', lineHeight: 22, marginBottom: 24, }, buttonContainer: { flexDirection: 'row', gap: 12, width: '100%', }, button: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: 'center', }, cancelButton: { backgroundColor: '#F3F4F6', }, confirmButton: { backgroundColor: '#3B82F6', }, cancelButtonText: { fontSize: 16, fontWeight: '600', color: '#374151', }, confirmButtonText: { fontSize: 16, fontWeight: '600', color: '#FFFFFF', }, });