From a8b4f17043328f8a5e23f9b4fcfca5bd5e2d3b4f Mon Sep 17 00:00:00 2001 From: ddshi <8811906+ddshi@user.noreply.gitee.com> Date: Tue, 3 Feb 2026 14:14:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=8F=90=E9=86=92?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E6=A0=B7=E5=BC=8F=E5=92=8C=E5=BD=92=E6=A1=A3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化提醒卡片样式,统一黑白灰配色 - 添加checkbox勾选动画和过期提醒淡出效果 - 完善归档页功能(恢复/删除已过期完成提醒) - 修复过期检测逻辑(精确到时间点而非仅日期) Co-Authored-By: Claude (MiniMax-M2.1) --- src/components/reminder/ReminderCard.tsx | 131 +++++++++++++++++------ src/components/reminder/ReminderList.tsx | 5 +- src/pages/ArchivePage.tsx | 29 +++-- tmpclaude-a0eb-cwd | 1 - tmpclaude-bde2-cwd | 1 - 5 files changed, 120 insertions(+), 47 deletions(-) delete mode 100644 tmpclaude-a0eb-cwd delete mode 100644 tmpclaude-bde2-cwd diff --git a/src/components/reminder/ReminderCard.tsx b/src/components/reminder/ReminderCard.tsx index cccd632..7b1c28a 100644 --- a/src/components/reminder/ReminderCard.tsx +++ b/src/components/reminder/ReminderCard.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useMemo, useState, useEffect } from 'react'; import { Paper, Text, Checkbox, Group, Stack, ActionIcon } from '@mantine/core'; import { IconDots } from '@tabler/icons-react'; import type { Event } from '../../types'; @@ -8,13 +8,15 @@ interface ReminderCardProps { onToggle: () => void; onClick: () => void; onDelete?: () => void; + isMissed?: boolean; } -export function ReminderCard({ event, onToggle, onClick }: ReminderCardProps) { +export function ReminderCard({ event, onToggle, onClick, isMissed = false }: ReminderCardProps) { const isCompleted = event.is_completed ?? false; const [isHovered, setIsHovered] = useState(false); + const [isAnimating, setIsAnimating] = useState(false); - // 计算距离提醒时间的相关显示 + // 计算时间信息 const timeInfo = useMemo(() => { const now = new Date(); const eventDate = new Date(event.date); @@ -22,7 +24,6 @@ export function ReminderCard({ event, onToggle, onClick }: ReminderCardProps) { const isPast = diff < 0; const isToday = eventDate.toDateString() === now.toDateString(); - // 格式化时间显示 const timeStr = eventDate.toLocaleString('zh-CN', { month: 'numeric', day: 'numeric', @@ -33,12 +34,32 @@ export function ReminderCard({ event, onToggle, onClick }: ReminderCardProps) { return { isPast, isToday, timeStr, diff }; }, [event.date]); - // 颜色主题 - const getThemeColor = () => { + // 获取文字颜色 + const getTextColor = () => { if (isCompleted) return '#999'; + if (timeInfo.isPast) return '#666'; + return '#1a1a1a'; + }; + + // 获取时间颜色 + const getTimeColor = () => { + if (isCompleted) return '#bbb'; if (timeInfo.isPast) return '#c41c1c'; - if (timeInfo.isToday) return '#666'; - return '#888'; + return '#666'; + }; + + // 获取背景色 + const getBackground = () => { + if (isCompleted) return 'rgba(0, 0, 0, 0.02)'; + if (isMissed) return 'rgba(196, 28, 28, 0.03)'; + return 'rgba(0, 0, 0, 0.02)'; + }; + + // 获取边框颜色 + const getBorderColor = () => { + if (isCompleted) return 'rgba(0, 0, 0, 0.06)'; + if (isMissed) return 'rgba(196, 28, 28, 0.15)'; + return 'rgba(0, 0, 0, 0.06)'; }; return ( @@ -49,26 +70,55 @@ export function ReminderCard({ event, onToggle, onClick }: ReminderCardProps) { onMouseLeave={() => setIsHovered(false)} style={{ cursor: 'pointer', - opacity: isCompleted ? 0.4 : 1, - transition: 'all 0.2s ease', + opacity: isAnimating ? 0 : isCompleted ? 0.4 : 1, transform: isHovered ? 'translateY(-1px)' : 'translateY(0)', - borderLeft: `2px solid ${getThemeColor()}`, - background: 'rgba(0, 0, 0, 0.02)', - border: '1px solid rgba(0, 0, 0, 0.04)', + background: getBackground(), + border: `1px solid ${getBorderColor()}`, + borderLeft: isMissed && !isCompleted ? '3px solid #c41c1c' : undefined, + transition: 'all 0.3s ease', + animation: isAnimating ? 'reminder-card-fadeOut 0.3s ease-out forwards' : 'none', }} > + - {/* Checkbox - 点击切换完成状态 */} - { - e.stopPropagation(); - onToggle(); + {/* Checkbox */} +
+ > + { + e.stopPropagation(); + if (isMissed && !isCompleted) { + // 已过期提醒:先播放动画,动画结束后再触发 toggle + setIsAnimating(true); + setTimeout(() => { + setIsAnimating(false); + onToggle(); + }, 300); + } else { + onToggle(); + } + }} + size="xs" + color="#1a1a1a" + /> +
{/* Title */} @@ -78,23 +128,34 @@ export function ReminderCard({ event, onToggle, onClick }: ReminderCardProps) { lineClamp={1} style={{ textDecoration: isCompleted ? 'line-through' : 'none', - color: isCompleted ? '#bbb' : '#1a1a1a', + color: getTextColor(), letterSpacing: '0.03em', + transition: 'color 0.2s ease', }} > {event.title} - {/* Time and content */} - - - {timeInfo.timeStr} - - + {/* Time */} + + {timeInfo.timeStr} + - {/* Content preview */} - {event.content && !isCompleted && ( - + {/* Content preview - 始终显示 */} + {event.content && ( + {event.content} )} @@ -111,7 +172,11 @@ export function ReminderCard({ event, onToggle, onClick }: ReminderCardProps) { e.stopPropagation(); onClick(); }} - style={{ color: '#999' }} + style={{ + color: '#999', + opacity: isHovered ? 1 : 0, + transition: 'all 0.2s ease', + }} title="编辑" > diff --git a/src/components/reminder/ReminderList.tsx b/src/components/reminder/ReminderList.tsx index 8807ac7..d328c85 100644 --- a/src/components/reminder/ReminderList.tsx +++ b/src/components/reminder/ReminderList.tsx @@ -83,8 +83,8 @@ export function ReminderList({ result.today.sort(sortByDate); result.tomorrow.sort(sortByDate); result.later.sort(sortByDate); - // 已过期按时间倒序(最近的在上面) - result.missed.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + // 已过期按时间正序(最早的在前) + result.missed.sort(sortByDate); return result; }, [events]); @@ -176,6 +176,7 @@ export function ReminderList({ onClick={() => onEventClick(event)} onToggle={() => onToggleComplete(event)} onDelete={onDelete ? () => onDelete(event) : undefined} + isMissed={true} /> ))} {grouped.missed.length > 3 && ( diff --git a/src/pages/ArchivePage.tsx b/src/pages/ArchivePage.tsx index f1c1c34..bce8ed4 100644 --- a/src/pages/ArchivePage.tsx +++ b/src/pages/ArchivePage.tsx @@ -8,6 +8,7 @@ import { Group, Button, ActionIcon, + Box, } from '@mantine/core'; import { IconArrowLeft, IconRotateClockwise, IconTrash, IconArchive } from '@tabler/icons-react'; import { useNavigate } from 'react-router-dom'; @@ -17,21 +18,27 @@ import type { Event } from '../types'; export function ArchivePage() { const navigate = useNavigate(); const events = useAppStore((state) => state.events); + const fetchEvents = useAppStore((state) => state.fetchEvents); const updateEventById = useAppStore((state) => state.updateEventById); const deleteEventById = useAppStore((state) => state.deleteEventById); - // 页面加载时检查登录状态 + // 页面加载时获取数据 useEffect(() => { const isAuthenticated = useAppStore.getState().isAuthenticated; if (!isAuthenticated) { navigate('/login', { replace: true }); + } else { + fetchEvents(); } - }, [navigate]); + }, [navigate, fetchEvents]); // 获取已归档的提醒(已过期且已勾选的) - const archivedReminders = events.filter( - (e) => e.type === 'reminder' && e.is_completed - ).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + const archivedReminders = events.filter((e) => { + if (e.type !== 'reminder' || !e.is_completed) return false; + const eventDate = new Date(e.date); + const now = new Date(); + return eventDate < now; // 仅已过期的 + }).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); const handleRestore = async (event: Event) => { await updateEventById(event.id, { is_completed: false }); @@ -42,15 +49,17 @@ export function ArchivePage() { }; return ( -
- + {/* Header */} - +
+ ); } diff --git a/tmpclaude-a0eb-cwd b/tmpclaude-a0eb-cwd deleted file mode 100644 index 094e142..0000000 --- a/tmpclaude-a0eb-cwd +++ /dev/null @@ -1 +0,0 @@ -/e/qia/client diff --git a/tmpclaude-bde2-cwd b/tmpclaude-bde2-cwd deleted file mode 100644 index 094e142..0000000 --- a/tmpclaude-bde2-cwd +++ /dev/null @@ -1 +0,0 @@ -/e/qia/client