From f0cbd0e33c5ae2218816ddc42bde70916b46cfd4 Mon Sep 17 00:00:00 2001 From: ddshi <8811906+ddshi@user.noreply.gitee.com> Date: Thu, 5 Feb 2026 17:51:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=8F=90=E9=86=92?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E5=8F=B3=E9=94=AE=E8=8F=9C=E5=8D=95=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化checkbox样式:缩小尺寸(14px)、移除阴影、添加白色填充 - 调整布局:标题和内容左对齐 - 重构右键菜单为垂直分类布局:调整时间/颜色/操作 - 添加菜单边缘保护:自动计算位置避免超出浏览器 - 添加点击外部和ESC键关闭菜单 - 编辑弹窗优先级改为颜色圆点选择器 - 添加priority类型定义 🤖 Generated with Claude Code --- src/components/reminder/ReminderCard.tsx | 772 ++++++++++++++++------- src/components/reminder/ReminderList.tsx | 18 +- src/pages/HomePage.tsx | 112 +++- src/stores/contextMenu.ts | 40 ++ src/types/index.ts | 4 + 5 files changed, 722 insertions(+), 224 deletions(-) create mode 100644 src/stores/contextMenu.ts diff --git a/src/components/reminder/ReminderCard.tsx b/src/components/reminder/ReminderCard.tsx index 5350d5a..7eb9d0b 100644 --- a/src/components/reminder/ReminderCard.tsx +++ b/src/components/reminder/ReminderCard.tsx @@ -1,8 +1,47 @@ -import { useMemo, useState } from 'react'; -import { Paper, Text, Checkbox, Group, Stack, ActionIcon, Box } from '@mantine/core'; -import { IconDots, IconArrowForward, IconRepeat, IconRepeatOff } from '@tabler/icons-react'; -import type { Event, RepeatType } from '../../types'; +import { useMemo, useState, useCallback, useEffect, useRef } from 'react'; +import { + Paper, + Text, + Group, + Stack, + ActionIcon, + Box, + Menu, + Divider, + Portal, +} from '@mantine/core'; +import { + IconDots, + IconArrowForward, + IconRepeat, + IconCalendar, + IconEdit, + IconTrash, + IconCircleFilled, + IconCheck, +} from '@tabler/icons-react'; +import type { Event, PriorityType } from '../../types'; import { getRepeatTypeLabel } from '../../utils/repeatCalculator'; +import { useContextMenuStore } from '../../stores/contextMenu'; + +// 重复图标颜色常量 +const REPEAT_ICON_COLOR = '#999'; + +// 优先级颜色映射 +const PRIORITY_COLORS: Record = { + none: 'rgba(0, 0, 0, 0.15)', + red: '#dc2626', + green: '#16a34a', + yellow: '#ca8a04', +}; + +// 优先级名称映射 +const PRIORITY_NAMES: Record = { + none: '默认', + red: '红色', + green: '绿色', + yellow: '黄色', +}; interface ReminderCardProps { event: Event; @@ -12,12 +51,99 @@ interface ReminderCardProps { onPostpone?: () => void; isMissed?: boolean; onMissedToggle?: () => void; + onDateChange?: (date: string, repeatType: string, repeatInterval: number | null) => void; + onPriorityChange?: (priority: PriorityType) => void; } -export function ReminderCard({ event, onToggle, onClick, onDelete, onPostpone, isMissed = false, onMissedToggle }: ReminderCardProps) { +export function ReminderCard({ + event, + onToggle, + onClick, + onDelete, + onPostpone, + isMissed = false, + onMissedToggle, + onDateChange, + onPriorityChange, +}: ReminderCardProps) { const isCompleted = event.is_completed ?? false; const [isHovered, setIsHovered] = useState(false); const [isAnimating, setIsAnimating] = useState(false); + const cardRef = useRef(null); + + // 使用全局状态管理右键菜单 + const openEventId = useContextMenuStore((state) => state.openEventId); + const { openMenu, closeMenu } = useContextMenuStore(); + const isMenuOpen = openEventId === event.id; + + // 获取优先级颜色 + const getPriorityColor = (priority?: PriorityType): string => { + return PRIORITY_COLORS[priority || 'none']; + }; + + // 右键点击处理 + const handleContextMenu = useCallback((e: React.MouseEvent) => { + e.preventDefault(); + console.log('[ContextMenu] 右键点击事件, eventId:', event.id); + + // 打开菜单 + openMenu(event.id); + }, [event.id, openMenu]); + + // 点击外部关闭菜单 + useEffect(() => { + if (!isMenuOpen) return; + + const handleMouseDown = (e: MouseEvent) => { + const target = e.target as HTMLElement; + // 如果点击不在当前卡片和菜单内,关闭菜单 + if (!target.closest(`[data-reminder-card="${event.id}"]`) && + !target.closest('[data-context-menu]')) { + closeMenu(); + } + }; + + // ESC键关闭菜单 + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + closeMenu(); + } + }; + + document.addEventListener('mousedown', handleMouseDown); + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('mousedown', handleMouseDown); + document.removeEventListener('keydown', handleKeyDown); + }; + }, [isMenuOpen, event.id, closeMenu]); + + // 计算菜单位置(带边缘保护) + const getMenuPosition = () => { + if (!cardRef.current) return { left: 0, top: 0 }; + + const cardRect = cardRef.current.getBoundingClientRect(); + const menuWidth = 180; // 菜单宽度 + const menuHeight = 150; // 估算菜单高度 + const padding = 8; // 边缘padding + + // 计算左侧位置 + let left = cardRect.left; + if (left + menuWidth > window.innerWidth - padding) { + left = window.innerWidth - menuWidth - padding; + } + if (left < padding) left = padding; + + // 计算顶部位置(优先显示在下方) + let top = cardRect.bottom + 4; + if (top + menuHeight > window.innerHeight - padding) { + // 如果下方空间不足,显示在上方 + top = cardRect.top - menuHeight - 4; + } + if (top < padding) top = padding; + + return { left, top }; + }; // 计算时间信息 const timeInfo = useMemo(() => { @@ -33,7 +159,6 @@ export function ReminderCard({ event, onToggle, onClick, onDelete, onPostpone, i const eventDate = new Date(dateStr); // 检查是否包含有效时间(小时和分钟不全为0) - // 对于本地格式(如 "2025-02-05T00:00:00")和 ISO 格式都适用 const hours = eventDate.getHours(); const minutes = eventDate.getMinutes(); const hasExplicitTime = hours !== 0 || minutes !== 0; @@ -45,7 +170,6 @@ export function ReminderCard({ event, onToggle, onClick, onDelete, onPostpone, i // 格式化显示 let timeStr: string; if (hasExplicitTime) { - // 有设置时间,显示日期+时间 timeStr = eventDate.toLocaleString('zh-CN', { month: 'numeric', day: 'numeric', @@ -54,7 +178,6 @@ export function ReminderCard({ event, onToggle, onClick, onDelete, onPostpone, i hour12: false, }); } else { - // 没有设置时间(00:00),只显示日期 timeStr = eventDate.toLocaleString('zh-CN', { month: 'numeric', day: 'numeric', @@ -92,156 +215,213 @@ export function ReminderCard({ event, onToggle, onClick, onDelete, onPostpone, i return 'rgba(0, 0, 0, 0.06)'; }; - // 获取循环图标颜色 - const getRepeatIconColor = (type: RepeatType) => { - const colors: Record = { - daily: '#3b82f6', // 蓝色 - weekly: '#22c55e', // 绿色 - monthly: '#a855f7', // 紫色 - yearly: '#f59e0b', // 橙色 - none: '#999', + // 获取优先级的checkbox边框样式 + const getCheckboxBorderStyle = () => { + const priority = event.priority || 'none'; + const priorityColor = getPriorityColor(priority); + const isHighPriority = priority !== 'none'; + return { + borderColor: priorityColor, + borderWidth: isHighPriority ? '2px' : '1px', + borderStyle: 'solid' as const, }; - return colors[type] || '#999'; }; + // 处理日期调整 + const handleDateAdjust = useCallback((days: number) => { + const now = new Date(); + const targetDate = new Date(now); + targetDate.setDate(targetDate.getDate() + days); + + // 格式化为 YYYY-MM-DD + const formattedDate = targetDate.toISOString().split('T')[0]; + onDateChange?.(formattedDate, event.repeat_type, event.repeat_interval ?? null); + closeMenu(); + }, [onDateChange, event.repeat_type, event.repeat_interval, closeMenu]); + + // 处理优先级设置 + const handlePriorityChange = useCallback((priority: PriorityType) => { + onPriorityChange?.(priority); + closeMenu(); + }, [onPriorityChange, closeMenu]); + return ( - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - style={{ - cursor: 'pointer', - opacity: isAnimating ? 0 : isCompleted ? 0.4 : 1, - transform: isHovered ? 'translateY(-1px)' : 'translateY(0)', - 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', - }} - > - - {/* 正常提醒卡片:左右分栏布局 */} - {!isMissed ? ( - - {/* 左侧:Checkbox + 标题 + 内容 */} - { - // 阻止事件冒泡到 Card,避免重复触发 - e.stopPropagation(); - onClick(); - }} - > - - {/* Checkbox - 单独处理,不触发卡片点击 */} -
{ - // 阻止事件冒泡,避免触发卡片点击 - e.stopPropagation(); - }} - > - { + <> + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + onContextMenu={handleContextMenu} + data-reminder-card={event.id} + style={{ + cursor: 'pointer', + opacity: isAnimating ? 0 : isCompleted ? 0.4 : 1, + transform: isHovered ? 'translateY(-1px)' : 'translateY(0)', + 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', + }} + > + + {/* 正常提醒卡片:左右分栏布局 */} + {!isMissed ? ( + + {/* 左侧:Checkbox + 标题 + 内容 */} + { + e.stopPropagation(); + onClick(); + }} + > + + {/* Checkbox */} +
{ e.stopPropagation(); onToggle(); }} - size="xs" - color="#1a1a1a" + > + {/* 已完成时显示的对勾 */} + {isCompleted && ( + + ✓ + + )} +
+ + {/* 标题和内容垂直排列 */} + + {/* 标题 */} + + {event.title} + + + {/* 内容在标题下方 */} + {event.content && ( + + {event.content} + + )} + +
+
+ + {/* 右侧:循环图标 + 日期时间 */} + + {event.repeat_type !== 'none' && ( + -
- - {/* 标题 */} - - {event.title} - -
- - {/* 内容在标题下方 */} - {event.content && ( + title={getRepeatTypeLabel(event.repeat_type)} + > + +
+ )} - {event.content} + {timeInfo.timeStr} - )} - - - {/* 右侧:循环图标 + 日期时间 */} - - {event.repeat_type !== 'none' && ( - +
+ ) : ( + /* 逾期提醒卡片 */ + + +
- - - )} - - {timeInfo.timeStr} - - - - ) : ( - /* 逾期提醒卡片:保持原有结构 */ - - -
- { + onClick={(e) => { e.stopPropagation(); if (isMissed && !isCompleted) { setIsAnimating(true); @@ -254,103 +434,257 @@ export function ReminderCard({ event, onToggle, onClick, onDelete, onPostpone, i onToggle(); } }} - size="xs" - color="#1a1a1a" - /> -
- - - - - {event.title} - - {event.repeat_type !== 'none' && ( - - - - )} - - - {timeInfo.timeStr} - - {event.content && ( + {/* 已完成时显示的对勾 */} + {isCompleted && ( + + ✓ + + )} +
+ + + + + {event.title} + + {event.repeat_type !== 'none' && ( + + + + )} + - {event.content} + {timeInfo.timeStr} - )} - -
+ {event.content && ( + + {event.content} + + )} + +
- - {onPostpone && ( + + {onPostpone && ( + { + e.stopPropagation(); + onPostpone(); + }} + style={{ + color: '#e67e22', + opacity: isHovered ? 1 : 0, + transition: 'all 0.2s ease', + }} + title="顺延到今天" + > + + + )} { e.stopPropagation(); - onPostpone(); + onClick(); }} style={{ - color: '#e67e22', + color: '#999', opacity: isHovered ? 1 : 0, transition: 'all 0.2s ease', }} - title="顺延到今天" + title="编辑" > - + - )} - { - e.stopPropagation(); - onClick(); - }} - style={{ - color: '#999', - opacity: isHovered ? 1 : 0, - transition: 'all 0.2s ease', - }} - title="编辑" - > - - + - + )} +
+ + {/* 右键菜单 - 使用Portal渲染到body */} + {isMenuOpen && cardRef.current && ( + + e.stopPropagation()} + > + {/* 调整时间 */} + + + 调整时间 + + + handleDateAdjust(0)} + > + 今天 + + handleDateAdjust(1)} + > + 明天 + + handleDateAdjust(7)} + > + 一周后 + + + + + + + {/* 颜色 */} + + + 颜色 + + + {(Object.keys(PRIORITY_COLORS) as PriorityType[]).map((priority) => ( + handlePriorityChange(priority)} + > + + {PRIORITY_NAMES[priority]} + + + ))} + + + + + + {/* 操作 */} + + { + onClick(); + closeMenu(); + }} + > + 编辑 + + { + onDelete?.(); + closeMenu(); + }} + > + 删除 + + + + )} - + ); } diff --git a/src/components/reminder/ReminderList.tsx b/src/components/reminder/ReminderList.tsx index 7af93df..31c41ec 100644 --- a/src/components/reminder/ReminderList.tsx +++ b/src/components/reminder/ReminderList.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef, useState, useEffect } from 'react'; +import { useMemo, useRef, useState } from 'react'; import { Stack, Text, @@ -18,7 +18,7 @@ import { } from '@tabler/icons-react'; import { useNavigate } from 'react-router-dom'; import { ReminderCard } from './ReminderCard'; -import type { Event } from '../../types'; +import type { Event, PriorityType } from '../../types'; interface ReminderListProps { events: Event[]; @@ -27,6 +27,8 @@ interface ReminderListProps { onAddClick: () => void; onDelete?: (event: Event) => void; onPostpone?: (event: Event) => void; + onDateChange?: (event: Event, date: string, repeatType: string, repeatInterval: number | null) => void; + onPriorityChange?: (event: Event, priority: PriorityType) => void; } export function ReminderList({ @@ -36,6 +38,8 @@ export function ReminderList({ onAddClick, onDelete, onPostpone, + onDateChange, + onPriorityChange, }: ReminderListProps) { const navigate = useNavigate(); const scrollContainerRef = useRef(null); @@ -299,6 +303,8 @@ export function ReminderList({ onDelete={onDelete ? () => onDelete(event) : undefined} onPostpone={onPostpone ? () => onPostpone(event) : undefined} onMissedToggle={triggerArchiveShake} + onDateChange={onDateChange ? (date, repeatType, repeatInterval) => onDateChange(event, date, repeatType, repeatInterval) : undefined} + onPriorityChange={onPriorityChange ? (priority) => onPriorityChange(event, priority) : undefined} isMissed={true} /> ))} @@ -329,6 +335,8 @@ export function ReminderList({ onDelete={onDelete ? () => onDelete(event) : undefined} onPostpone={onPostpone ? () => onPostpone(event) : undefined} onMissedToggle={triggerArchiveShake} + onDateChange={onDateChange ? (date, repeatType, repeatInterval) => onDateChange(event, date, repeatType, repeatInterval) : undefined} + onPriorityChange={onPriorityChange ? (priority) => onPriorityChange(event, priority) : undefined} isMissed={true} /> ))} @@ -376,6 +384,8 @@ export function ReminderList({ onClick={() => onEventClick(event)} onToggle={() => onToggleComplete(event)} onDelete={onDelete ? () => onDelete(event) : undefined} + onDateChange={onDateChange ? (date, repeatType, repeatInterval) => onDateChange(event, date, repeatType, repeatInterval) : undefined} + onPriorityChange={onPriorityChange ? (priority) => onPriorityChange(event, priority) : undefined} /> ))} @@ -405,6 +415,8 @@ export function ReminderList({ onClick={() => onEventClick(event)} onToggle={() => onToggleComplete(event)} onDelete={onDelete ? () => onDelete(event) : undefined} + onDateChange={onDateChange ? (date, repeatType, repeatInterval) => onDateChange(event, date, repeatType, repeatInterval) : undefined} + onPriorityChange={onPriorityChange ? (priority) => onPriorityChange(event, priority) : undefined} /> ))} @@ -434,6 +446,8 @@ export function ReminderList({ onClick={() => onEventClick(event)} onToggle={() => onToggleComplete(event)} onDelete={onDelete ? () => onDelete(event) : undefined} + onDateChange={onDateChange ? (date, repeatType, repeatInterval) => onDateChange(event, date, repeatType, repeatInterval) : undefined} + onPriorityChange={onPriorityChange ? (priority) => onPriorityChange(event, priority) : undefined} /> ))} diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index c49929e..2c2ca48 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -11,6 +11,7 @@ import { Switch, Select, Stack, + Box, } from '@mantine/core'; import { DatePickerInput, TimeInput } from '@mantine/dates'; import { IconLogout, IconSettings } from '@tabler/icons-react'; @@ -21,7 +22,7 @@ import { AnniversaryList } from '../components/anniversary/AnniversaryList'; import { ReminderList } from '../components/reminder/ReminderList'; import { NoteEditor } from '../components/note/NoteEditor'; import { FloatingAIChat } from '../components/ai/FloatingAIChat'; -import type { Event, EventType, RepeatType } from '../types'; +import type { Event, EventType, RepeatType, PriorityType } from '../types'; import { calculateNextReminderDate } from '../utils/repeatCalculator'; export function HomePage() { @@ -48,6 +49,7 @@ export function HomePage() { const [formIsLunar, setFormIsLunar] = useState(false); const [formRepeatType, setFormRepeatType] = useState('none'); const [formIsHoliday, setFormIsHoliday] = useState(false); + const [formPriority, setFormPriority] = useState('none'); // Initialize auth and data on mount useEffect(() => { @@ -70,6 +72,7 @@ export function HomePage() { setFormIsLunar(event.is_lunar); setFormRepeatType(event.repeat_type); setFormIsHoliday(event.is_holiday || false); + setFormPriority(event.priority || 'none'); // 提取时间(如果日期中包含时间信息) const eventDate = new Date(event.date); const hours = eventDate.getHours(); @@ -89,6 +92,7 @@ export function HomePage() { setFormIsLunar(false); setFormRepeatType('none'); setFormIsHoliday(type === 'anniversary'); + setFormPriority('none'); open(); }; @@ -123,6 +127,7 @@ export function HomePage() { // 计算下一次提醒日期 next_reminder_date: calculateNextReminderDate(dateStr, formRepeatType, undefined), is_holiday: formIsHoliday ?? false, + priority: formPriority, }; // 只有当 content 有值时才包含 @@ -180,14 +185,80 @@ export function HomePage() { // 构建新的本地时间字符串 const newDateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}T${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`; - const result = await updateEventById(event.id, { + // 构建更新数据 + const updateData: Record = { date: newDateStr, - }); + priority: event.priority || 'none', + }; + + // 如果是重复提醒,同步更新 next_reminder_date + if (event.repeat_type && event.repeat_type !== 'none') { + const nextReminderDate = calculateNextReminderDate( + newDateStr, + event.repeat_type as RepeatType, + event.repeat_interval || undefined + ); + updateData.next_reminder_date = nextReminderDate; + } + + const result = await updateEventById(event.id, updateData); if (result.error) { console.error('顺延失败:', result.error); } }; + const handleDateChange = async ( + event: Event, + date: string, + repeatType: string, + repeatInterval: number | null + ) => { + if (event.type !== 'reminder') return; + // 保留原事件的时间信息 + const originalDate = new Date(event.date); + const hours = originalDate.getHours(); + const minutes = originalDate.getMinutes(); + + // 构建新的本地时间字符串 + const newDateStr = `${date}T${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`; + + // 构建更新数据 + const updateData: Record = { + date: newDateStr, + priority: event.priority || 'none', + }; + + // 如果是重复提醒,同步更新 next_reminder_date + if (repeatType && repeatType !== 'none') { + const nextReminderDate = calculateNextReminderDate( + newDateStr, + repeatType as RepeatType, + repeatInterval || undefined + ); + updateData.next_reminder_date = nextReminderDate; + } + + // 合并更新 + const result = await updateEventById(event.id, updateData); + if (result.error) { + console.error('日期调整失败:', result.error); + } + fetchEvents(); // 刷新列表以更新UI + }; + + const handlePriorityChange = async (event: Event, priority: PriorityType) => { + if (event.type !== 'reminder') return; + // 合并更新 priority 和 date,确保 updated_at 被更新 + const result = await updateEventById(event.id, { + priority, + date: event.date, + }); + if (result.error) { + console.error('优先级设置失败:', result.error); + } + fetchEvents(); // 刷新列表以更新UI + }; + const resetForm = () => { setFormType('anniversary'); setFormTitle(''); @@ -197,6 +268,7 @@ export function HomePage() { setFormIsLunar(false); setFormRepeatType('none'); setFormIsHoliday(false); + setFormPriority('none'); setSelectedEvent(null); setIsEdit(false); }; @@ -286,6 +358,8 @@ export function HomePage() { onAddClick={() => handleAddClick('reminder')} onDelete={handleDelete} onPostpone={handlePostpone} + onDateChange={handleDateChange} + onPriorityChange={handlePriorityChange} /> @@ -460,6 +534,38 @@ export function HomePage() { }} /> + {/* Priority (only for reminders) */} + {formType === 'reminder' && ( + + + 优先级 + + + {([ + { value: 'none' as const, color: 'rgba(0, 0, 0, 0.15)' }, + { value: 'red' as const, color: '#dc2626' }, + { value: 'green' as const, color: '#16a34a' }, + { value: 'yellow' as const, color: '#ca8a04' }, + ].map((item) => ( + setFormPriority(item.value)} + style={{ + width: 24, + height: 24, + borderRadius: '50%', + background: item.color, + border: formPriority === item.value ? '2px solid #1a1a1a' : '1px solid rgba(0, 0, 0, 0.1)', + cursor: 'pointer', + transition: 'all 0.2s ease', + transform: formPriority === item.value ? 'scale(1.1)' : 'scale(1)', + }} + /> + )))} + + + )} + {/* Holiday switch (only for anniversaries) */} {formType === 'anniversary' && ( void; + // 关闭菜单 + closeMenu: () => void; + // 切换菜单 + toggleMenu: (eventId: string) => void; + // 点击其他位置关闭菜单 + closeOnClickOutside: (eventId: string) => void; +} + +export const useContextMenuStore = create((set) => ({ + openEventId: null, + + openMenu: (eventId: string) => { + set({ openEventId: eventId }); + }, + + closeMenu: () => { + set({ openEventId: null }); + }, + + toggleMenu: (eventId: string) => { + set((state) => ({ + openEventId: state.openEventId === eventId ? null : eventId, + })); + }, + + // 点击其他位置时,如果点击的是不同的事件ID则关闭菜单 + closeOnClickOutside: (clickedEventId: string) => { + set((state) => ({ + openEventId: state.openEventId === clickedEventId ? state.openEventId : null, + })); + }, +})); diff --git a/src/types/index.ts b/src/types/index.ts index a9a3217..6857423 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -13,6 +13,9 @@ export type EventType = 'anniversary' | 'reminder'; // Repeat types: 'daily'每天, 'weekly'每周, 'monthly'每月, 'yearly'每年, 'none'不重复 export type RepeatType = 'daily' | 'weekly' | 'monthly' | 'yearly' | 'none'; +// Priority types: 'none'无色, 'red'红色, 'green'绿色, 'yellow'黄色 +export type PriorityType = 'none' | 'red' | 'green' | 'yellow'; + // Unified event type (matches backend API) export interface Event { id: string; @@ -27,6 +30,7 @@ export interface Event { next_reminder_date?: string | null; // 下一次提醒日期(计算用) is_holiday?: boolean; // Only for anniversaries is_completed?: boolean; // Only for reminders + priority?: PriorityType; // Priority level for reminders created_at: string; updated_at: string; }