feat(Theme): enhance theming and UI consistency across components
- Updated app.json to set userInterfaceStyle to automatic for improved theme adaptability. - Refactored layout components to utilize useAppColors for dynamic theming, ensuring consistent color usage. - Introduced SystemChrome component to manage system UI background color based on theme. - Enhanced TabsLayout, ProfileStackLayout, and other components to leverage new theming structure. - Improved QRCodeScanner, SearchBar, and CommentItem styles to align with the updated theme system. - Consolidated styles in SystemMessageItem and TabBar for better maintainability and visual coherence.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { View, Text, StyleSheet, TouchableOpacity, Alert, ScrollView, Pressable } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useRouter, useLocalSearchParams } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
|
||||
import { colors, spacing, borderRadius, fontSizes, shadows } from '../../theme';
|
||||
import { spacing, borderRadius, fontSizes, shadows, useAppColors, type AppColors } from '../../theme';
|
||||
import { routePayloadCache } from '../../stores/routePayloadCache';
|
||||
import { scheduleService } from '../../services/scheduleService';
|
||||
|
||||
@@ -32,15 +32,146 @@ const formatWeekRanges = (weeks: number[]) => {
|
||||
return ranges.join(', ');
|
||||
};
|
||||
|
||||
function createCourseDetailStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
mask: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.32)',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: spacing.lg,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: colors.background.paper,
|
||||
borderRadius: borderRadius.xl,
|
||||
paddingVertical: spacing.lg,
|
||||
paddingHorizontal: spacing.lg,
|
||||
maxHeight: '82%',
|
||||
...shadows.lg,
|
||||
},
|
||||
contentScroll: {
|
||||
maxHeight: '100%',
|
||||
},
|
||||
contentScrollContainer: {
|
||||
paddingBottom: spacing.xs,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: spacing.lg,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: `${colors.divider}AA`,
|
||||
paddingBottom: spacing.md,
|
||||
},
|
||||
headerLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconBadge: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: borderRadius.full,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: `${colors.primary.main}12`,
|
||||
marginRight: spacing.sm,
|
||||
},
|
||||
title: {
|
||||
fontSize: fontSizes.xl,
|
||||
fontWeight: '700',
|
||||
color: colors.text.primary,
|
||||
},
|
||||
closeButton: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: borderRadius.full,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
row: {
|
||||
marginBottom: spacing.lg,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
rowLast: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
label: {
|
||||
width: 76,
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '600',
|
||||
color: colors.text.secondary,
|
||||
lineHeight: 22,
|
||||
marginTop: 1,
|
||||
},
|
||||
value: {
|
||||
flex: 1,
|
||||
fontSize: fontSizes.lg,
|
||||
fontWeight: '600',
|
||||
color: colors.text.primary,
|
||||
lineHeight: 24,
|
||||
},
|
||||
timeList: {
|
||||
flex: 1,
|
||||
gap: spacing.sm,
|
||||
},
|
||||
timeItemCard: {
|
||||
borderWidth: 1,
|
||||
borderColor: `${colors.primary.main}20`,
|
||||
borderRadius: borderRadius.md,
|
||||
backgroundColor: `${colors.primary.main}08`,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingVertical: spacing.sm,
|
||||
},
|
||||
timeItemMain: {
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '600',
|
||||
color: colors.text.primary,
|
||||
lineHeight: 20,
|
||||
},
|
||||
timeItemSub: {
|
||||
marginTop: spacing.xs,
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.secondary,
|
||||
lineHeight: 18,
|
||||
},
|
||||
actionRow: {
|
||||
marginTop: spacing.lg,
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
deleteButton: {
|
||||
height: 38,
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: borderRadius.full,
|
||||
backgroundColor: `${colors.error.main}10`,
|
||||
borderWidth: 1,
|
||||
borderColor: `${colors.error.main}25`,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs,
|
||||
},
|
||||
deleteButtonText: {
|
||||
fontSize: fontSizes.sm,
|
||||
fontWeight: '700',
|
||||
color: colors.error.main,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const CourseDetailScreen: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const colors = useAppColors();
|
||||
const styles = useMemo(() => createCourseDetailStyles(colors), [colors]);
|
||||
const { courseId } = useLocalSearchParams<{ courseId?: string }>();
|
||||
const cached = courseId ? routePayloadCache.getCourseDetail(courseId) : undefined;
|
||||
const course = cached?.course;
|
||||
const relatedCourses = cached?.relatedCourses ?? [];
|
||||
|
||||
const timeLines =
|
||||
relatedCourses.length > 0 ? relatedCourses : course ? [course] : [];
|
||||
const timeLines = relatedCourses.length > 0 ? relatedCourses : course ? [course] : [];
|
||||
|
||||
const handleDeleteCourse = () => {
|
||||
if (!course) return;
|
||||
@@ -98,7 +229,9 @@ const CourseDetailScreen: React.FC = () => {
|
||||
>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>课程名称</Text>
|
||||
<Text style={styles.value} numberOfLines={2}>{course.name}</Text>
|
||||
<Text style={styles.value} numberOfLines={2}>
|
||||
{course.name}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={[styles.row, styles.rowLast]}>
|
||||
@@ -110,9 +243,7 @@ const CourseDetailScreen: React.FC = () => {
|
||||
第{formatWeekRanges(item.weeks)}周 · {WEEKDAY_NAMES[item.dayOfWeek]} 第
|
||||
{getMergedSectionIndex(item.startSection)}节
|
||||
</Text>
|
||||
<Text style={styles.timeItemSub}>
|
||||
任课教师:{item.teacher || '未填写'}
|
||||
</Text>
|
||||
<Text style={styles.timeItemSub}>任课教师:{item.teacher || '未填写'}</Text>
|
||||
{item.location ? <Text style={styles.timeItemSub}>{item.location}</Text> : null}
|
||||
</View>
|
||||
))}
|
||||
@@ -132,132 +263,4 @@ const CourseDetailScreen: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
mask: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.32)',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: spacing.lg,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: colors.background.paper,
|
||||
borderRadius: borderRadius.xl,
|
||||
paddingVertical: spacing.lg,
|
||||
paddingHorizontal: spacing.lg,
|
||||
maxHeight: '82%',
|
||||
...shadows.lg,
|
||||
},
|
||||
contentScroll: {
|
||||
maxHeight: '100%',
|
||||
},
|
||||
contentScrollContainer: {
|
||||
paddingBottom: spacing.xs,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: spacing.lg,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: `${colors.divider}AA`,
|
||||
paddingBottom: spacing.md,
|
||||
},
|
||||
headerLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconBadge: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: borderRadius.full,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: `${colors.primary.main}12`,
|
||||
marginRight: spacing.sm,
|
||||
},
|
||||
title: {
|
||||
fontSize: fontSizes.xl,
|
||||
fontWeight: '700',
|
||||
color: colors.text.primary,
|
||||
},
|
||||
closeButton: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: borderRadius.full,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
row: {
|
||||
marginBottom: spacing.lg,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
rowLast: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
label: {
|
||||
width: 76,
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '600',
|
||||
color: colors.text.secondary,
|
||||
lineHeight: 22,
|
||||
marginTop: 1,
|
||||
},
|
||||
value: {
|
||||
flex: 1,
|
||||
fontSize: fontSizes.lg,
|
||||
fontWeight: '600',
|
||||
color: colors.text.primary,
|
||||
lineHeight: 24,
|
||||
},
|
||||
timeList: {
|
||||
flex: 1,
|
||||
gap: spacing.sm,
|
||||
},
|
||||
timeItemCard: {
|
||||
borderWidth: 1,
|
||||
borderColor: `${colors.primary.main}20`,
|
||||
borderRadius: borderRadius.md,
|
||||
backgroundColor: `${colors.primary.main}08`,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingVertical: spacing.sm,
|
||||
},
|
||||
timeItemMain: {
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '600',
|
||||
color: colors.text.primary,
|
||||
lineHeight: 20,
|
||||
},
|
||||
timeItemSub: {
|
||||
marginTop: spacing.xs,
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.secondary,
|
||||
lineHeight: 18,
|
||||
},
|
||||
actionRow: {
|
||||
marginTop: spacing.lg,
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
deleteButton: {
|
||||
height: 38,
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: borderRadius.full,
|
||||
backgroundColor: `${colors.error.main}10`,
|
||||
borderWidth: 1,
|
||||
borderColor: `${colors.error.main}25`,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs,
|
||||
},
|
||||
deleteButtonText: {
|
||||
fontSize: fontSizes.sm,
|
||||
fontWeight: '700',
|
||||
color: colors.error.main,
|
||||
},
|
||||
});
|
||||
|
||||
export default CourseDetailScreen;
|
||||
|
||||
@@ -23,7 +23,14 @@ import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { colors, fontSizes, spacing, borderRadius, shadows } from '../../theme';
|
||||
import {
|
||||
fontSizes,
|
||||
spacing,
|
||||
borderRadius,
|
||||
shadows,
|
||||
useAppColors,
|
||||
type AppColors,
|
||||
} from '../../theme';
|
||||
import { useResponsive } from '../../hooks/useResponsive';
|
||||
import {
|
||||
Course,
|
||||
@@ -148,6 +155,8 @@ const getWeekDates = (weekOffset: number = 0) => {
|
||||
const INITIAL_WEEK = getCurrentWeekNumber();
|
||||
|
||||
export const ScheduleScreen: React.FC = () => {
|
||||
const colors = useAppColors();
|
||||
const styles = useMemo(() => createScheduleStyles(colors), [colors]);
|
||||
const router = useRouter();
|
||||
// 使用响应式 hook 检测屏幕尺寸
|
||||
const { width: screenWidth, height: screenHeight, isMobile, platform } = useResponsive();
|
||||
@@ -1063,7 +1072,8 @@ export const ScheduleScreen: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
function createScheduleStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.background.paper,
|
||||
@@ -1483,6 +1493,7 @@ const styles = StyleSheet.create({
|
||||
color: colors.text.secondary,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default ScheduleScreen;
|
||||
|
||||
Reference in New Issue
Block a user