事件管理指南

事件是 Day Flow 日历的核心数据结构。本文将介绍如何创建、更新、删除和管理日历事件。

事件接口定义

本库使用 Temporal API 处理所有日期/时间相关操作。事件支持三种 Temporal 类型:

  • PlainDate:适用于全天事件(不包含具体时间)
  • PlainDateTime:适用于本地事件(日期+时间,无时区) 推荐在大多数场景使用
  • ZonedDateTime:适用于时区敏感事件(国际会议、航班等)
import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';
属性类型描述必需
idstring事件的唯一标识符必填
titlestring在日历中显示的事件标题必填
startTemporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime事件开始日期/时间。使用 PlainDate 表示全天,PlainDateTime 表示本地时间必填
endTemporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime事件结束日期/时间必填
descriptionstring可选的事件描述或备注可选
allDayboolean是否为全天事件(默认:false可选
iconboolean | Node事件的自定义图标。true(默认):显示默认,false:隐藏,Node:自定义图标可选
calendarIdstring单日历归属的日历类型引用可选
calendarIdsstring[]事件所属的日历 ID 列表。设置后优先于 calendarId。只要其中任意一个日历可见,事件就会显示。渲染时使用多色斜纹图案背景。可选
metaRecord<string, any>额外的自定义元数据(位置、参与者、自定义字段等)可选

创建事件

简单事件创建(推荐)

在大多数场景下,使用 createEvent() 辅助函数:

import {
  createEvent,
  createAllDayEvent,
  createTimedEvent,
} from '@dayflow/core';
import '@dayflow/core/dist/styles.css';

// 本地定时事件(无时区复杂性)
const meeting = createEvent({
  id: '1',
  title: '团队会议',
  start: new Date(2024, 9, 15, 10, 0), // 2024年10月15日 10:00
  end: new Date(2024, 9, 15, 11, 0), // 2024年10月15日 11:00
});

// 全天事件
const holiday = createAllDayEvent('2', '技术大会', new Date(2024, 9, 20));

// 快速创建定时事件
const lunch = createTimedEvent(
  '3',
  '午餐休息',
  new Date(2024, 9, 15, 12, 0), // 12:00
  new Date(2024, 9, 15, 13, 0) // 13:00
);

高级用法:直接使用 Temporal API

如需更精细的控制,可直接使用 Temporal API:

import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';

// 使用 PlainDateTime 的本地事件(推荐)
const localEvent: Event = {
  id: '1',
  title: '团队会议',
  start: Temporal.PlainDateTime.from({
    year: 2024,
    month: 10,
    day: 15,
    hour: 10,
    minute: 0,
  }),
  end: Temporal.PlainDateTime.from({
    year: 2024,
    month: 10,
    day: 15,
    hour: 11,
    minute: 0,
  }),
};

// 使用 PlainDate 的全天事件
const allDayEvent: Event = {
  id: '2',
  title: '技术大会',
  start: Temporal.PlainDate.from('2024-10-20'),
  end: Temporal.PlainDate.from('2024-10-22'),
  allDay: true,
};

// 使用时区感知的 ZonedDateTime 事件
const timezoneEvent: Event = {
  id: '3',
  title: '国际电话会议',
  start: Temporal.ZonedDateTime.from('2024-10-16T14:00:00[America/New_York]'),
  end: Temporal.ZonedDateTime.from('2024-10-16T15:00:00[America/New_York]'),
};

包含元数据的事件

import { createEvent } from '@dayflow/core';

const event = createEvent({
  id: '3',
  title: '客户电话',
  description: '讨论第四季度路线图',
  start: new Date(2024, 9, 16, 14, 0),
  end: new Date(2024, 9, 16, 15, 0),
  meta: {
    location: 'Zoom 会议',
    attendees: ['zhangsan@example.com', 'lisi@example.com'],
    recurring: false,
  },
});

事件管理

添加事件

// 添加单个事件
calendar.addEvent(event);

// 初始化时添加多个事件const calendar = useCalendarApp({
  views: [createMonthView()],
  events: [event1, event2, event3],
});

更新事件

// 更新事件
calendar.updateEvent('event-id', {
  title: '更新后的会议标题',
  start: new Date(2024, 9, 15, 11, 0),
  end: new Date(2024, 9, 15, 12, 0),
});

// 使用待处理状态更新(适用于调整大小操作)
calendar.updateEvent('event-id', updatedEvent, true);

删除事件

// 根据 ID 删除事件
calendar.deleteEvent('event-id');

获取事件

// 获取所有事件const events = calendar.getEvents();

// 从状态中获取当前事件const { events } = calendar;

事件回调函数

Day Flow 提供回调函数来处理事件生命周期:

import { useCalendarApp, createMonthView, Event } from '@dayflow/core';

const calendar = useCalendarApp({
  views: [createMonthView()],
  events: initialEvents,
  callbacks: {
    onEventCreate: (event: Event) => {
      console.log('新事件已创建:', event);
      // 与后端同步
      api.createEvent(event);
    },
    onEventUpdate: (event: Event) => {
      console.log('事件已更新:', event);
      // 与后端同步
      api.updateEvent(event);
    },
    onEventDelete: (eventId: string) => {
      console.log('事件已删除:', eventId);
      // 同步后端
      api.deleteEvent(eventId);
    },
    onEventDoubleClick: (event: Event, e: MouseEvent) => {
      console.log('事件被双击:', event);
      // 使用 e.currentTarget 作为自定义弹出层的锚点
    },
  },
});

事件状态管理

使用框架状态

您可以结合所使用框架(React, Vue, Svelte, Angular)的原生状态管理来维护事件。

import { useState } from 'react';
import {
  useCalendarApp,
  DayFlowCalendar,
  createMonthView,
  Event,
} from '@dayflow/core';

function MyCalendar() {
  const [events, setEvents] = useState<Event[]>([]);

  const calendar = useCalendarApp({
    views: [createMonthView()],
    events,
    callbacks: {
      onEventCreate: (event: Event) => {
        setEvents(prev => [...prev, event]);
      },
      onEventUpdate: (event: Event) => {
        setEvents(prev => prev.map(e => (e.id === event.id ? event : e)));
      },
      onEventDelete: (eventId: string) => {
        setEvents(prev => prev.filter(e => e.id !== eventId));
      },
    },
  });

  return <DayFlowCalendar calendar={calendar} />;
}

与后端同步

import { useCalendarApp, createMonthView, Event } from '@dayflow/core';

const calendar = useCalendarApp({
  views: [createMonthView()],
  events,
  callbacks: {
    onEventCreate: async (event: Event) => {
      try {
        // 在后端创建事件
        const savedEvent = await api.createEvent(event);

        // 使用后端响应更新本地状态
        setEvents(prev => [...prev, savedEvent]);
      } catch (error) {
        console.error('创建事件失败:', error);
        // 可选:回滚乐观更新
      }
    },
    onEventUpdate: async (event: Event) => {
      try {
        await api.updateEvent(event);
        setEvents(prev => prev.map(e => (e.id === event.id ? event : e)));
      } catch (error) {
        console.error('更新事件失败:', error);
      }
    },
    onEventDelete: async (eventId: string) => {
      try {
        await api.deleteEvent(eventId);
        setEvents(prev => prev.filter(e => e.id !== eventId));
      } catch (error) {
        console.error('删除事件失败:', error);
      }
    },
  },
});

通过日历类型设置事件样式

通过 calendarId 将事件分配到不同的日历类型来自定义外观。每个日历类型都可以有自己的配色方案和样式:

import { createEvent } from '@dayflow/core';

// 工作事件
const workEvent = createEvent({
  id: '1',
  title: '设计评审',
  start: new Date(2024, 9, 15, 14, 0),
  end: new Date(2024, 9, 15, 15, 0),
  calendarId: 'work',// 关联工作日历样式});

// 个人事件
const personalEvent = createEvent({
  id: '2',
  title: '牙医预约',
  start: new Date(2024, 9, 16, 10, 0),
  end: new Date(2024, 9, 16, 11, 0),
  calendarId: 'personal',// 关联个人日历样式});

// 配置带颜色的日历类型
const calendars = [
  {
    id: 'work',
    name: '工作',
    colors: {
      eventColor: '#3b82f6',
      eventSelectedColor: '#2563eb',
      lineColor: '#3b82f6',
      textColor: '#ffffff',
    },
    isVisible: true,
  },
  {
    id: 'personal',
    name: '个人',
    colors: {
      eventColor: '#10b981',
      eventSelectedColor: '#059669',
      lineColor: '#10b981',
      textColor: '#ffffff',
    },
    isVisible: true,
  },
];

const calendar = useCalendarApp({
  views: [createMonthView()],
  events: [workEvent, personalEvent],
  calendars,
});

多日历事件

工作原理

  • calendarIds 设置后将优先于 calendarId
  • 可见性:只要列表中任意一个日历可见,事件就会显示。
  • 视觉区分:多日历事件使用45° 斜纹图案背景(每种颜色对应一个日历),左侧色条显示多色渐变,与单日历事件一眼区分。
  • 选中状态:回退为主日历(calendarIds[0])的纯色背景。

示例

import { createEvent, useCalendarApp, createWeekView } from '@dayflow/core';

// 同时归属于 "team" 和 "marketing" 两个日历
const sharedEvent = createEvent({
  id: 'shared-1',
  title: '公司全员会议',
  start: new Date(2024, 9, 15, 10, 0),
  end: new Date(2024, 9, 15, 11, 30),
  calendarIds: ['team', 'marketing'], // 多日历
});

// 跨三个日历的全天事件
const crossCalendarDay = {
  id: 'shared-2',
  title: '团队外出',
  start: Temporal.PlainDate.from('2024-10-20'),
  end: Temporal.PlainDate.from('2024-10-22'),
  allDay: true,
  calendarIds: ['team', 'personal', 'travel'],
};

const calendars = [
  {
    id: 'team',
    name: '团队',
    colors: {
      eventColor: '#3b82f6',
      lineColor: '#3b82f6',
      eventSelectedColor: '#2563eb',
      textColor: '#fff',
    },
    isVisible: true,
  },
  {
    id: 'marketing',
    name: '市场',
    colors: {
      eventColor: '#f59e0b',
      lineColor: '#f59e0b',
      eventSelectedColor: '#d97706',
      textColor: '#fff',
    },
    isVisible: true,
  },
  {
    id: 'personal',
    name: '个人',
    colors: {
      eventColor: '#10b981',
      lineColor: '#10b981',
      eventSelectedColor: '#059669',
      textColor: '#fff',
    },
    isVisible: true,
  },
  {
    id: 'travel',
    name: '旅行',
    colors: {
      eventColor: '#8b5cf6',
      lineColor: '#8b5cf6',
      eventSelectedColor: '#7c3aed',
      textColor: '#fff',
    },
    isVisible: true,
  },
];

const calendar = useCalendarApp({
  views: [createWeekView()],
  events: [sharedEvent, crossCalendarDay],
  calendars,
});

跨多天事件

事件可以跨越多个日期:

import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';

// 持续3天的会议(全天事件)
const multiDayEvent: Event = {
  id: '1',
  title: '2024技术大会',
  start: Temporal.PlainDate.from('2024-10-20'),
  end: Temporal.PlainDate.from('2024-10-22'),
  allDay: true,
  calendarId: 'conferences',
};

// 跨越午夜的事件(定时事件)
const crossMidnightEvent: Event = {
  id: '2',
  title: '夜班',
  start: Temporal.ZonedDateTime.from('2024-10-15T22:00:00[America/New_York]'), // 晚上10点
  end: Temporal.ZonedDateTime.from('2024-10-16T06:00:00[America/New_York]'), // 次日早上6点
  calendarId: 'shifts',
};

事件元数据

meta 字段中存储额外信息:

import { Temporal } from 'temporal-polyfill';
import { Event } from '@dayflow/core';

const event: Event = {
  id: '1',
  title: '项目启动会',
  start: Temporal.ZonedDateTime.from('2024-10-15T10:00:00[America/New_York]'),
  end: Temporal.ZonedDateTime.from('2024-10-15T11:00:00[America/New_York]'),
  meta: {
    // 会议详情
    location: 'A会议室',
    meetingUrl: 'https://zoom.us/j/123456',

    // 参与人
    organizer: 'zhangsan@example.com',
    attendees: ['lisi@example.com', 'wangwu@example.com'],

    // 自定义字段
    project: 'X项目',
    priority: 'high',
    tags: ['规划', '启动会'],

    // 重复信息
    recurring: true,
    recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO',

    // 其他任意数据
    customField: '自定义值',
  },
};

最佳实践

  1. 始终提供唯一ID - 使用 UUID 或数据库 ID(字符串格式)
  2. 使用辅助函数 - 优先使用 createEvent() 而非手动构建 Temporal 对象(覆盖90%的使用场景)
  3. 选择正确的类型
    • 全天事件(生日、节假日)使用 PlainDate
    • 本地事件(会议、预约)使用 PlainDateTime 推荐默认选项
    • 仅时区敏感事件(国际电话、航班)使用 ZonedDateTime
  4. 验证时间值 - 辅助函数会自动验证小时(0-23)和分钟(0-59)
  5. 使用日历类型进行样式设置 - 分配 calendarId 来分类事件并保持样式一致性;当事件跨多个日历时使用 calendarIds
  6. 善用 meta 字段 - 存储自定义数据而无需修改 Event 接口
  7. 验证事件数据 - 确保定时事件的开始时间早于结束时间
  8. 优化事件更新 - 尽可能批量更新

类型参考

有关日期/时间处理的详细信息,请参阅:

  • 事件接口:/src/types/event.ts

相关文档

On this page