feat: 优化图片加载和展示功能

- 新增 SmartImage 组件优化图片加载体验
- 新增 ImageGrid 组件支持多图布局展示
- 新增 imageHelper 工具函数
- 优化 PostCard 帖子卡片图片展示
- 优化消息页面 SegmentRenderer
- 优化日程页面 ScheduleScreen
- 优化上传服务 uploadService
This commit is contained in:
2026-03-15 02:25:55 +08:00
parent e9d42fdb01
commit 592167e667
8 changed files with 299 additions and 46 deletions

View File

@@ -145,6 +145,8 @@ export const ScheduleScreen: React.FC = () => {
const [startWeekInput, setStartWeekInput] = useState(String(currentWeek));
const [endWeekInput, setEndWeekInput] = useState(String(TOTAL_WEEKS));
const weekScrollRef = useRef<ScrollView | null>(null);
// 组件卸载状态标记,用于防止组件卸载后仍调用 setState
const isMountedRef = useRef(true);
// 设置弹窗状态
const [isSettingsModalVisible, setIsSettingsModalVisible] = useState(false);
@@ -153,6 +155,13 @@ export const ScheduleScreen: React.FC = () => {
const [syncUsername, setSyncUsername] = useState('');
const [syncPassword, setSyncPassword] = useState('');
const [isSyncing, setIsSyncing] = useState(false);
// 组件卸载时设置标记
useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
const todayColumnIndex = getTodayColumnIndex();
// currentWeek === 1 对应今天所在的真实周offset 0其他周不高亮今日列
const isViewingCurrentWeek = currentWeek === 1;
@@ -463,14 +472,31 @@ export const ScheduleScreen: React.FC = () => {
style: 'destructive' as const,
onPress: async () => {
try {
// 删除所有相关课程
for (const relatedCourse of relatedCourses) {
await scheduleService.deleteCourse(relatedCourse.id);
// 使用 Promise.allSettled 批量删除,即使部分失败也能继续
const results = await Promise.allSettled(
relatedCourses.map(course => scheduleService.deleteCourse(course.id))
);
// 统计成功和失败的数量
const fulfilledCount = results.filter(r => r.status === 'fulfilled').length;
const rejectedCount = results.filter(r => r.status === 'rejected').length;
// 只移除成功删除的课程
const successfullyDeletedIds = new Set(
relatedCourses
.filter((_, index) => results[index].status === 'fulfilled')
.map(c => c.id)
);
setCourses(prev => prev.filter(c => !successfullyDeletedIds.has(c.id)));
if (rejectedCount > 0) {
Alert.alert(
'部分删除成功',
`成功删除 ${fulfilledCount} 节课程,${rejectedCount} 节删除失败,请重试`
);
} else {
Alert.alert('成功', `已删除 ${fulfilledCount} 节课程`);
}
// 从本地状态中移除
const relatedIds = new Set(relatedCourses.map(c => c.id));
setCourses(prev => prev.filter(c => !relatedIds.has(c.id)));
Alert.alert('成功', `已删除 ${relatedCourses.length} 节课程`);
} catch (error) {
console.error('批量删除课程失败:', error);
Alert.alert('删除失败', '请稍后重试');
@@ -755,14 +781,17 @@ export const ScheduleScreen: React.FC = () => {
]}
onPress={async () => {
if (!syncUsername.trim() || !syncPassword.trim()) return;
setIsSyncing(true);
try {
const result = await scheduleService.syncSchedule({
username: syncUsername.trim(),
password: syncPassword.trim(),
});
// 检查组件是否已卸载
if (!isMountedRef.current) return;
if (result.success) {
Alert.alert(
'同步成功',
@@ -787,9 +816,15 @@ export const ScheduleScreen: React.FC = () => {
}
} catch (error) {
console.error('同步课表失败:', error);
Alert.alert('同步失败', '网络错误,请稍后重试');
// 检查组件是否已卸载
if (isMountedRef.current) {
Alert.alert('同步失败', '网络错误,请稍后重试');
}
} finally {
setIsSyncing(false);
// 检查组件是否已卸载
if (isMountedRef.current) {
setIsSyncing(false);
}
}
}}
activeOpacity={0.8}