You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
3.8 KiB
171 lines
3.8 KiB
/** |
|
* 公告栏组件 |
|
* |
|
* 展示滚动公告,使用真实数据 |
|
*/ |
|
|
|
import React, { useState, useEffect, useRef } from 'react'; |
|
import { |
|
View, |
|
Text, |
|
StyleSheet, |
|
Animated, |
|
TouchableOpacity, |
|
Dimensions, |
|
} from 'react-native'; |
|
import { createThemeStyles } from '@/theme'; |
|
import Colors from '@/constants/Colors'; |
|
import { mockNotices } from '@/services/mockHomeService'; |
|
import type { Notice } from '@/types/home'; |
|
|
|
const { width } = Dimensions.get('window'); |
|
|
|
/** |
|
* 创建主题样式 |
|
*/ |
|
const styles = createThemeStyles((colors) => ({ |
|
container: { |
|
backgroundColor: colors.backgroundSecondary, |
|
paddingVertical: 10, |
|
paddingHorizontal: 12, |
|
marginHorizontal: 12, |
|
marginBottom: 12, |
|
borderRadius: 8, |
|
flexDirection: 'row', |
|
alignItems: 'center', |
|
elevation: 2, |
|
shadowColor: colors.cardShadow, |
|
shadowOffset: { width: 0, height: 1 }, |
|
shadowOpacity: 0.1, |
|
shadowRadius: 3, |
|
}, |
|
label: { |
|
fontSize: 12, |
|
fontWeight: 'bold', |
|
color: '#FFFFFF', |
|
marginRight: 8, |
|
backgroundColor: colors.primary, |
|
paddingHorizontal: 8, |
|
paddingVertical: 4, |
|
borderRadius: 4, |
|
}, |
|
content: { |
|
flex: 1, |
|
fontSize: 12, |
|
color: colors.text, |
|
overflow: 'hidden', |
|
}, |
|
closeButton: { |
|
marginLeft: 8, |
|
padding: 4, |
|
}, |
|
closeText: { |
|
fontSize: 16, |
|
color: colors.textSecondary, |
|
}, |
|
})); |
|
|
|
interface NoticeBarProps { |
|
theme: 'light' | 'dark'; |
|
notices?: Notice[]; |
|
onNoticePress?: (notice: Notice) => void; |
|
} |
|
|
|
/** |
|
* 公告栏组件 |
|
*/ |
|
export default function NoticeBar({ theme, notices: propNotices, onNoticePress }: NoticeBarProps) { |
|
const s = styles[theme]; |
|
const [notices, setNotices] = useState<Notice[]>(propNotices || []); |
|
const [currentNotice, setCurrentNotice] = useState(0); |
|
const [visible, setVisible] = useState(true); |
|
const animatedValue = useRef(new Animated.Value(1)).current; |
|
|
|
// 加载公告数据 |
|
useEffect(() => { |
|
if (propNotices && propNotices.length > 0) { |
|
setNotices(propNotices); |
|
return; |
|
} |
|
|
|
const loadNotices = async () => { |
|
try { |
|
// const data = await getNotices(); |
|
// setNotices(data.length > 0 ? data : mockNotices); |
|
} catch (error) { |
|
console.error('加载公告失败:', error); |
|
setNotices(mockNotices); |
|
} |
|
}; |
|
loadNotices(); |
|
}, [propNotices]); |
|
|
|
// 自动切换公告 |
|
useEffect(() => { |
|
if (notices.length === 0) return; |
|
|
|
const timer = setInterval(() => { |
|
setCurrentNotice((prev) => (prev + 1) % notices.length); |
|
}, 5000); |
|
|
|
return () => clearInterval(timer); |
|
}, [notices.length]); |
|
|
|
// 处理关闭公告 |
|
const handleClose = () => { |
|
Animated.timing(animatedValue, { |
|
toValue: 0, |
|
duration: 300, |
|
useNativeDriver: true, |
|
}).start(() => { |
|
setVisible(false); |
|
}); |
|
}; |
|
|
|
// 处理公告点击 |
|
const handleNoticePress = () => { |
|
if (notices.length > 0) { |
|
onNoticePress?.(notices[currentNotice]); |
|
} |
|
}; |
|
|
|
if (!visible || notices.length === 0) { |
|
return null; |
|
} |
|
|
|
const currentNoticeData = notices[currentNotice]; |
|
|
|
return ( |
|
<Animated.View |
|
style={[ |
|
s.container, |
|
{ |
|
opacity: animatedValue, |
|
transform: [ |
|
{ |
|
scaleY: animatedValue, |
|
}, |
|
], |
|
}, |
|
]} |
|
> |
|
<Text style={s.label}>📢</Text> |
|
<TouchableOpacity |
|
style={{ flex: 1 }} |
|
onPress={handleNoticePress} |
|
activeOpacity={0.7} |
|
> |
|
<Text style={s.content} numberOfLines={1}> |
|
{currentNoticeData.title || currentNoticeData.content} |
|
</Text> |
|
</TouchableOpacity> |
|
<TouchableOpacity |
|
style={s.closeButton} |
|
onPress={handleClose} |
|
> |
|
<Text style={s.closeText}>✕</Text> |
|
</TouchableOpacity> |
|
</Animated.View> |
|
); |
|
} |
|
|
|
|