Files
digital-pilates/components/ui/InfoCard.tsx
richarjiang f4ce3d9edf feat(medications): 重构药品通知系统并添加独立设置页面
- 创建药品通知服务模块,统一管理药品提醒通知的调度和取消
- 新增独立的通知设置页面,支持总开关和药品提醒开关分离控制
- 重构药品详情页面,移除频率编辑功能到独立页面
- 优化药品添加流程,支持拍照和相册选择图片
- 改进通知权限检查和错误处理机制
- 更新用户偏好设置,添加药品提醒开关配置
2025-11-11 16:43:27 +08:00

175 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Colors } from '@/constants/Colors';
import { Ionicons } from '@expo/vector-icons';
import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect';
import React from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export interface InfoCardProps {
label: string;
value: string;
icon: keyof typeof Ionicons.glyphMap;
colors: (typeof Colors)[keyof typeof Colors];
onPress?: () => void;
clickable?: boolean;
glassEffectStyle?: 'clear' | 'regular';
tintColor?: string;
className?: string;
}
export const InfoCard: React.FC<InfoCardProps> = ({
label,
value,
icon,
colors,
onPress,
clickable = false,
glassEffectStyle = 'clear',
tintColor,
}) => {
const isGlassAvailable = isLiquidGlassAvailable();
// 渲染图标按钮 - 只在可点击时应用 GlassView
const renderIcon = () => {
if (clickable && isGlassAvailable) {
return (
<GlassView
style={styles.infoCardIcon}
glassEffectStyle={glassEffectStyle}
tintColor={tintColor || 'rgba(76, 110, 245, 0.2)'}
isInteractive={true}
>
<Ionicons name={icon} size={16} color="#4C6EF5" />
</GlassView>
);
}
return (
<View style={[
styles.infoCardIcon,
clickable && styles.clickableIconFallback
]}>
<Ionicons name={icon} size={16} color="#4C6EF5" />
</View>
);
};
// 渲染箭头 - 只在可点击时显示并应用 GlassView
const renderArrow = () => {
if (!clickable) return null;
if (isGlassAvailable) {
return (
<GlassView
style={styles.infoCardArrow}
glassEffectStyle={glassEffectStyle}
tintColor={tintColor || 'rgba(255, 255, 255, 0.3)'}
isInteractive={true}
>
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
</GlassView>
);
}
return (
<View style={[styles.infoCardArrow, styles.arrowFallback]}>
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
</View>
);
};
// 卡片内容
const cardContent = (
<View style={[
styles.infoCard,
{ backgroundColor: colors.surface || '#fff' }
]}>
{renderArrow()}
{renderIcon()}
<Text style={[styles.infoCardLabel, { color: colors.textSecondary }]}>{label}</Text>
<Text style={[styles.infoCardValue, { color: colors.text }]}>{value}</Text>
</View>
);
// 如果可点击且有onPress回调使用TouchableOpacity包装
if (clickable && onPress) {
return (
<TouchableOpacity
style={styles.container}
onPress={onPress}
activeOpacity={0.7}
>
{cardContent}
</TouchableOpacity>
);
}
// 不可点击的版本
return (
<View style={styles.container}>
{cardContent}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
infoCard: {
flex: 1,
borderRadius: 20,
padding: 16,
backgroundColor: '#FFFFFF',
gap: 6,
shadowColor: '#000',
shadowOpacity: 0.06,
shadowRadius: 12,
shadowOffset: { width: 0, height: 3 },
elevation: 3,
position: 'relative',
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, 0.04)',
},
infoCardArrow: {
position: 'absolute',
top: 12,
right: 12,
zIndex: 1,
width: 24,
height: 24,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden', // 保证玻璃边界圆角效果
},
arrowFallback: {
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, 0.1)',
},
infoCardIcon: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: '#EEF1FF',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden', // 保证玻璃边界圆角效果
},
clickableIconFallback: {
borderWidth: 1,
borderColor: 'rgba(76, 110, 245, 0.3)',
},
infoCardLabel: {
fontSize: 13,
color: '#6B7280',
marginTop: 8,
},
infoCardValue: {
fontSize: 16,
fontWeight: '600',
color: '#1F2933',
},
});
export default InfoCard;