feat: 完善目标管理功能及相关组件
- 新增创建目标弹窗,支持用户输入目标信息并提交 - 实现目标数据的转换,支持将目标转换为待办事项和时间轴事件 - 优化目标页面,集成Redux状态管理,处理目标的创建、完成和错误提示 - 更新时间轴组件,支持动态显示目标安排 - 编写目标管理功能实现文档,详细描述功能和组件架构
This commit is contained in:
@@ -44,7 +44,8 @@ const getEventStyle = (event: TimelineEvent) => {
|
||||
const endMinutes = endTime.hour() * 60 + endTime.minute();
|
||||
const durationMinutes = endMinutes - startMinutes;
|
||||
|
||||
const top = (startMinutes / 60) * HOUR_HEIGHT;
|
||||
// 计算top位置时需要加上时间标签的paddingTop偏移(8px)
|
||||
const top = (startMinutes / 60) * HOUR_HEIGHT + 8;
|
||||
const height = Math.max((durationMinutes / 60) * HOUR_HEIGHT, 30); // 最小高度30
|
||||
|
||||
return { top, height };
|
||||
@@ -93,9 +94,13 @@ export function TimelineSchedule({ events, selectedDate, onEventPress }: Timelin
|
||||
const categoryColor = getCategoryColor(event.category);
|
||||
|
||||
// 计算水平偏移和宽度,用于处理重叠事件
|
||||
const eventWidth = (screenWidth - TIME_LABEL_WIDTH - 40) / Math.max(groupSize, 1);
|
||||
const availableWidth = screenWidth - TIME_LABEL_WIDTH - 48; // 减少一些边距
|
||||
const eventWidth = availableWidth / Math.max(groupSize, 1);
|
||||
const leftOffset = index * eventWidth;
|
||||
|
||||
// 判断是否应该显示时间段 - 当卡片高度小于50或宽度小于80时隐藏时间段
|
||||
const shouldShowTimeRange = height >= 50 && eventWidth >= 80;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={event.id}
|
||||
@@ -104,8 +109,8 @@ export function TimelineSchedule({ events, selectedDate, onEventPress }: Timelin
|
||||
{
|
||||
top,
|
||||
height,
|
||||
left: TIME_LABEL_WIDTH + 20 + leftOffset,
|
||||
width: eventWidth - 4,
|
||||
left: TIME_LABEL_WIDTH + 24 + leftOffset, // 调整左偏移对齐
|
||||
width: eventWidth - 8, // 增加卡片间距
|
||||
backgroundColor: event.isCompleted
|
||||
? `${categoryColor}40`
|
||||
: `${categoryColor}80`,
|
||||
@@ -124,15 +129,17 @@ export function TimelineSchedule({ events, selectedDate, onEventPress }: Timelin
|
||||
textDecorationLine: event.isCompleted ? 'line-through' : 'none'
|
||||
}
|
||||
]}
|
||||
numberOfLines={2}
|
||||
numberOfLines={shouldShowTimeRange ? 1 : 2} // 当显示时间时,标题只显示1行
|
||||
>
|
||||
{event.title}
|
||||
</Text>
|
||||
|
||||
<Text style={[styles.eventTime, { color: colorTokens.textSecondary }]}>
|
||||
{dayjs(event.startTime).format('HH:mm')}
|
||||
{event.endTime && ` - ${dayjs(event.endTime).format('HH:mm')}`}
|
||||
</Text>
|
||||
{shouldShowTimeRange && (
|
||||
<Text style={[styles.eventTime, { color: colorTokens.textSecondary }]}>
|
||||
{dayjs(event.startTime).format('HH:mm')}
|
||||
{event.endTime && ` - ${dayjs(event.endTime).format('HH:mm')}`}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{event.isCompleted && (
|
||||
<View style={styles.completedIcon}>
|
||||
@@ -146,22 +153,23 @@ export function TimelineSchedule({ events, selectedDate, onEventPress }: Timelin
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* 日期标题 */}
|
||||
<View style={styles.dateHeader}>
|
||||
<Text style={[styles.dateText, { color: colorTokens.text }]}>
|
||||
{dayjs(selectedDate).format('YYYY年M月D日 dddd')}
|
||||
</Text>
|
||||
<Text style={[styles.eventCount, { color: colorTokens.textSecondary }]}>
|
||||
{events.length} 项任务
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* 时间轴 */}
|
||||
<ScrollView
|
||||
style={styles.timelineContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={{ paddingBottom: 50 }}
|
||||
>
|
||||
{/* 日期标题 */}
|
||||
<View style={styles.dateHeader}>
|
||||
<Text style={[styles.dateText, { color: colorTokens.text }]}>
|
||||
{dayjs(selectedDate).format('YYYY年M月D日 dddd')}
|
||||
</Text>
|
||||
<Text style={[styles.eventCount, { color: colorTokens.textSecondary }]}>
|
||||
{events.length} 个目标
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={styles.timeline}>
|
||||
{/* 时间标签 */}
|
||||
<View style={styles.timeLabelsContainer}>
|
||||
@@ -220,7 +228,8 @@ function CurrentTimeLine() {
|
||||
|
||||
const now = dayjs();
|
||||
const currentMinutes = now.hour() * 60 + now.minute();
|
||||
const top = (currentMinutes / 60) * HOUR_HEIGHT;
|
||||
// 当前时间线也需要加上时间标签的paddingTop偏移(8px)
|
||||
const top = (currentMinutes / 60) * HOUR_HEIGHT + 8;
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -245,16 +254,14 @@ const styles = StyleSheet.create({
|
||||
dateHeader: {
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#E5E7EB',
|
||||
},
|
||||
dateText: {
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
marginBottom: 4,
|
||||
},
|
||||
eventCount: {
|
||||
fontSize: 14,
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
},
|
||||
timelineContainer: {
|
||||
@@ -283,7 +290,7 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
borderBottomWidth: 1,
|
||||
marginLeft: 8,
|
||||
marginRight: 20,
|
||||
marginRight: 24,
|
||||
},
|
||||
eventsContainer: {
|
||||
position: 'absolute',
|
||||
@@ -309,6 +316,7 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
padding: 8,
|
||||
justifyContent: 'space-between',
|
||||
|
||||
},
|
||||
eventTitle: {
|
||||
fontSize: 12,
|
||||
@@ -327,8 +335,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
currentTimeLine: {
|
||||
position: 'absolute',
|
||||
left: TIME_LABEL_WIDTH + 20,
|
||||
right: 20,
|
||||
left: TIME_LABEL_WIDTH + 24,
|
||||
right: 24,
|
||||
height: 2,
|
||||
zIndex: 10,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user