qia-client/src/pages/ArchivePage.tsx
ddshi 1559e603b0 feat: 实现重复提醒完成移除设置、逾期列表展开收起功能
- 重复提醒完成流程优化:
  - 勾选完成重复提醒后,自动移除repeat_type、repeat_interval、next_reminder_date
  - 自动创建下一周期的新提醒记录
  - 合并API调用,确保状态更新原子性

- 逾期列表展开/收起功能:
  - 默认收起,最多显示3条逾期提醒
  - 超过3条时显示"还有 X 个逾期提醒..."链接
  - 展开后底部显示"收起"按钮

- 时间显示优化:
  - 无时间提醒(00:00)只显示日期,不显示时间
  - 归档列表同样适用此规则

- 其他优化:
  - 归档抖动动画反馈
  - 分类折叠功能

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:51:38 +08:00

198 lines
5.9 KiB
TypeScript

import { useEffect } from 'react';
import {
Container,
Title,
Text,
Stack,
Paper,
Group,
Button,
ActionIcon,
Box,
} from '@mantine/core';
import { IconArrowLeft, IconRotateClockwise, IconTrash, IconArchive } from '@tabler/icons-react';
import { useNavigate } from 'react-router-dom';
import { useAppStore } from '../stores';
import type { Event } from '../types';
/**
* 格式化日期显示,根据是否有时间选择显示格式
*/
function formatArchiveDate(dateStr: string): string {
const date = new Date(dateStr);
const hours = date.getHours();
const minutes = date.getMinutes();
const hasExplicitTime = hours !== 0 || minutes !== 0;
if (hasExplicitTime) {
return date.toLocaleString('zh-CN', {
month: 'numeric',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: false,
});
} else {
return date.toLocaleString('zh-CN', {
month: 'numeric',
day: 'numeric',
});
}
}
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, fetchEvents]);
// 获取已归档的提醒(已过期且已勾选的)
const archivedReminders = events.filter((e) => {
if (e.type !== 'reminder' || !e.is_completed) return false;
if (!e.date) return false; // 跳过无日期的
const eventDate = new Date(e.date);
const now = new Date();
return eventDate < now; // 仅已过期的
}).sort((a, b) => {
if (!a.date || !b.date) return 0;
return new Date(b.date).getTime() - new Date(a.date).getTime();
});
const handleRestore = async (event: Event) => {
await updateEventById(event.id, { is_completed: false });
};
const handleDelete = async (event: Event) => {
await deleteEventById(event.id);
};
return (
<Box
style={{
minHeight: '100vh',
background: '#faf9f7',
paddingTop: 80,
paddingBottom: 40,
}}
>
<Container size="xs">
{/* Header */}
<Group mb="lg">
<Button
variant="subtle"
color="gray"
size="xs"
leftSection={<IconArrowLeft size={14} />}
onClick={() => navigate(-1)}
style={{
letterSpacing: '0.1em',
borderRadius: 2,
}}
>
</Button>
<Group gap="sm">
<IconArchive size={20} color="#666" />
<Title
order={2}
style={{
fontWeight: 300,
fontSize: '1.25rem',
letterSpacing: '0.15em',
color: '#1a1a1a',
}}
>
</Title>
</Group>
</Group>
{/* Content */}
<Paper p="md" withBorder radius={4}>
{archivedReminders.length === 0 ? (
<Stack align="center" justify="center" py="xl">
<Text c="#999" size="sm" ta="center" style={{ letterSpacing: '0.05em' }}>
</Text>
<Text size="xs" c="#bbb" ta="center" mt={4}>
</Text>
</Stack>
) : (
<Stack gap="sm">
{archivedReminders.map((event) => (
<Paper
key={event.id}
p="sm"
radius={2}
withBorder
style={{
background: 'rgba(0, 0, 0, 0.02)',
borderColor: 'rgba(0, 0, 0, 0.06)',
}}
>
<Group justify="space-between" wrap="nowrap">
<Stack gap={4} style={{ flex: 1 }}>
<Text
size="sm"
fw={400}
lineClamp={1}
style={{
textDecoration: 'line-through',
color: '#999',
letterSpacing: '0.03em',
}}
>
{event.title}
</Text>
<Text size="xs" c="#bbb">
{formatArchiveDate(event.date)}
</Text>
{event.content && (
<Text size="xs" c="#bbb" lineClamp={1}>
{event.content}
</Text>
)}
</Stack>
<Group gap={4}>
<ActionIcon
size="sm"
variant="subtle"
onClick={() => handleRestore(event)}
style={{ color: '#666' }}
title="恢复"
>
<IconRotateClockwise size={14} />
</ActionIcon>
<ActionIcon
size="sm"
variant="subtle"
onClick={() => handleDelete(event)}
style={{ color: '#999' }}
title="删除"
>
<IconTrash size={14} />
</ActionIcon>
</Group>
</Group>
</Paper>
))}
</Stack>
)}
</Paper>
</Container>
</Box>
);
}