qia-client/src/pages/ArchivePage.tsx
ddshi 306cb41516 feat: 优化编辑窗口UI
- 优化日期时间选择器为组合布局(同一行按钮)
- 优先级改名为颜色
- 提醒类型移除农历选项
- 使用Popover优化选择器交互
2026-02-06 13:44:07 +08:00

202 lines
6.1 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 today = new Date();
today.setHours(0, 0, 0, 0);
const eventDateOnly = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate());
// 仅显示已过期的(日期早于今天)
return eventDateOnly < today;
}).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>
);
}