119 lines
3.4 KiB
TypeScript
119 lines
3.4 KiB
TypeScript
import React from 'react'
|
|
import { View, Pressable, ViewProps, StyleSheet } from 'react-native'
|
|
import { LinearGradient } from 'expo-linear-gradient'
|
|
import Svg, { Path, Circle, Defs, Rect, LinearGradient as SvgLinearGradient, Stop } from 'react-native-svg'
|
|
import Text from './ui/Text'
|
|
|
|
interface ErrorStateProps extends ViewProps {
|
|
message?: string
|
|
onRetry?: () => void
|
|
/** 是否显示为空状态图标(默认)或错误图标 */
|
|
variant?: 'empty' | 'error'
|
|
}
|
|
|
|
/** 空状态图标 - 文件夹 */
|
|
function EmptyIcon() {
|
|
return (
|
|
<Svg width={80} height={80} viewBox="0 0 80 80" fill="none">
|
|
<Defs>
|
|
<SvgLinearGradient id="folderGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<Stop offset="0%" stopColor="#9966FF" stopOpacity={0.3} />
|
|
<Stop offset="100%" stopColor="#FF6699" stopOpacity={0.3} />
|
|
</SvgLinearGradient>
|
|
</Defs>
|
|
<Rect x="10" y="20" width="60" height="45" rx="6" fill="url(#folderGrad)" stroke="#4A4A4A" strokeWidth="2" />
|
|
<Path d="M10 26C10 22.6863 12.6863 20 16 20H30L36 14H64C67.3137 14 70 16.6863 70 20V26H10Z" fill="#2A2A2A" stroke="#4A4A4A" strokeWidth="2" />
|
|
<Circle cx="40" cy="45" r="12" stroke="#6B6B6B" strokeWidth="2" strokeDasharray="4 4" />
|
|
<Path d="M36 45H44M40 41V49" stroke="#6B6B6B" strokeWidth="2" strokeLinecap="round" />
|
|
</Svg>
|
|
)
|
|
}
|
|
|
|
/** 错误状态图标 */
|
|
function ErrorIcon() {
|
|
return (
|
|
<Svg width={80} height={80} viewBox="0 0 80 80" fill="none">
|
|
<Circle cx="40" cy="40" r="28" stroke="#FF6B6B" strokeWidth="2" strokeOpacity={0.5} />
|
|
<Circle cx="40" cy="40" r="20" fill="#2A2A2A" stroke="#FF6B6B" strokeWidth="2" />
|
|
<Path d="M40 30V44" stroke="#FF6B6B" strokeWidth="3" strokeLinecap="round" />
|
|
<Circle cx="40" cy="52" r="2" fill="#FF6B6B" />
|
|
</Svg>
|
|
)
|
|
}
|
|
|
|
export default function ErrorState({
|
|
message = 'An error occurred',
|
|
onRetry,
|
|
testID,
|
|
variant = 'empty',
|
|
...props
|
|
}: ErrorStateProps) {
|
|
return (
|
|
<View testID={testID} style={styles.container} {...props}>
|
|
{/* 图标 */}
|
|
<View style={styles.iconContainer}>
|
|
{variant === 'error' ? <ErrorIcon /> : <EmptyIcon />}
|
|
</View>
|
|
|
|
{/* 消息文本 */}
|
|
<Text style={styles.message}>{message}</Text>
|
|
|
|
{/* 重试按钮 - 使用渐变边框风格 */}
|
|
{onRetry && (
|
|
<Pressable onPress={onRetry} style={styles.retryButton}>
|
|
<LinearGradient
|
|
colors={['#9966FF', '#FF6699', '#FF9966']}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={styles.gradientBorder}
|
|
>
|
|
<View style={styles.buttonInner}>
|
|
<Text style={styles.buttonText}>重新加载</Text>
|
|
</View>
|
|
</LinearGradient>
|
|
</Pressable>
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 48,
|
|
minHeight: 280,
|
|
},
|
|
iconContainer: {
|
|
marginBottom: 20,
|
|
},
|
|
message: {
|
|
fontSize: 15,
|
|
color: '#8E8E93',
|
|
textAlign: 'center',
|
|
lineHeight: 22,
|
|
},
|
|
retryButton: {
|
|
marginTop: 24,
|
|
borderRadius: 22,
|
|
overflow: 'hidden',
|
|
},
|
|
gradientBorder: {
|
|
padding: 1.5,
|
|
borderRadius: 22,
|
|
},
|
|
buttonInner: {
|
|
backgroundColor: '#1C1E22',
|
|
paddingHorizontal: 28,
|
|
paddingVertical: 12,
|
|
borderRadius: 20,
|
|
},
|
|
buttonText: {
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
},
|
|
})
|