欢迎加入开源鸿蒙跨平台社区 :https://openharmonycrossplatform.csdn.net
项目基于 RN 0.72.90 开发
📋 前言
在移动应用开发中,日历事件管理是一项常见需求,特别是在日程管理、会议提醒、活动安排等场景中。react-native-calendar-events 是一个功能强大的日历事件库,提供了读取和写入系统日历的能力,支持创建、查询、更新和删除日历事件,是实现日程管理功能的理想选择。
🎯 库简介
基本信息
- 库名称 :
react-native-calendar-events - 版本信息 :
2.2.1支持 RN 0.72 版本 - 官方仓库: https://github.com/wmcmahan/react-native-calendar-events
- 鸿蒙仓库: https://atomgit.com/openharmony-sig/rntpc_react-native-calendar-events
- 主要功能 :
- 📅 日历权限管理
- 📆 日历增删改查
- 📝 事件创建与编辑
- ⏰ 提醒设置
- 🔁 重复规则配置
- 👥 参与者管理
- 📱 跨平台支持(iOS、Android、HarmonyOS)
为什么需要日历事件库?
| 特性 | 原生方案 | react-native-calendar-events |
|---|---|---|
| 跨平台一致性 | ⚠️ 需分别开发 | ✅ 统一 API |
| 权限管理 | ⚠️ 手动处理 | ✅ 自动处理 |
| 事件操作 | ⚠️ 需封装 | ✅ 开箱即用 |
| 重复规则 | ⚠️ 复杂实现 | ✅ 内置支持 |
| HarmonyOS 支持 | ⚠️ 需适配 | ✅ 完善适配 |
核心功能
| 功能 | 说明 | HarmonyOS 支持 |
|---|---|---|
| requestPermissions | 请求日历权限 | ✅ |
| checkPermissions | 检查权限状态 | ✅ |
| findCalendars | 获取所有日历 | ✅ |
| saveCalendar | 创建日历 | ✅ |
| removeCalendar | 删除日历 | ✅ |
| findEventById | 根据ID查询事件 | ✅ |
| fetchAllEvents | 获取所有事件 | ✅ |
| saveEvent | 创建/更新事件 | ✅ |
| removeEvent | 删除事件 | ✅ |
兼容性验证
在以下环境验证通过:
- RNOH : 0.72.90; SDK : HarmonyOS 6.0.0 Release SDK; IDE : DevEco Studio 6.0.2; ROM: 6.0.0
📦 安装步骤
1. 安装依赖
bash
# RN 0.72 版本
npm install @react-native-ohos/react-native-calendar-events@2.2.1-rc.1
# 或者使用 yarn
yarn add @react-native-ohos/react-native-calendar-events@2.2.1-rc.1
2. 验证安装
安装完成后,检查 package.json 文件:
json
{
"dependencies": {
"@react-native-ohos/react-native-calendar-events": "^2.2.1-rc.1"
}
}
🔧 HarmonyOS 平台配置 ⭐
1. 引入原生端代码
打开 harmony/entry/oh-package.json5,添加以下依赖:
json5
"dependencies": {
"@react-native-ohos/react-native-calendar-events": "file:../../node_modules/@react-native-ohos/react-native-calendar-events/harmony/calendar_events.har"
}
点击右上角的 sync 按钮,或者在终端执行:
bash
cd entry
ohpm install
2. 配置 CMakeLists
打开 entry/src/main/cpp/CMakeLists.txt,添加:
c
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
# RNOH_BEGIN: manual_package_linking_1
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-calendar-events/src/main/cpp" ./calendar_events)
# RNOH_END: manual_package_linking_1
# RNOH_BEGIN: manual_package_linking_2
+ target_link_libraries(rnoh_app PUBLIC rnoh_calendar_events)
# RNOH_END: manual_package_linking_2
3. 引入 CalendarEventPackage
打开 entry/src/main/cpp/PackageProvider.cpp,添加:
cpp
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "CalendarEventsPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
+ std::make_shared<CalendarEventsPackage>(ctx)
};
}
打开 entry/src/main/ets/RNPackagesFactory.ets,添加:
typescript
+ import { CalendarEventPackage } from '@react-native-ohos/react-native-calendar-events/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new SamplePackage(ctx),
+ new CalendarEventPackage(ctx)
];
}
4. 配置权限
在 entry/src/main/module.json5 中添加日历权限:
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.READ_CALENDAR",
"reason": "$string:Access_calendar",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.WRITE_CALENDAR",
"reason": "$string:Access_calendar",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
}
]
}
}
5. 添加权限说明
打开 entry/src/main/resources/base/element/string.json,添加权限说明:
json
{
"string": [
{
"name": "Access_calendar",
"value": "用于访问和管理日历事件"
}
]
}
📖 API 详解
requestPermissions - 请求日历权限
请求日历访问权限,必须在访问日历数据之前调用。
类型 :(readOnly?: boolean) => Promise<'authorized' | 'denied' | 'restricted' | 'undetermined'>
参数:
readOnly: 是否只请求读取权限,默认为false
返回值:
authorized: 已授权denied: 已拒绝restricted: 受限制(如家长控制)undetermined: 未确定
使用场景:
- 应用首次启动时请求权限
- 用户拒绝后重新请求
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const requestPermission = async () => {
const status = await RNCalendarEvents.requestPermissions();
if (status === 'authorized') {
console.log('日历权限已授权');
} else {
console.log('日历权限被拒绝');
}
};
checkPermissions - 检查权限状态
检查当前的日历权限状态,不会弹出权限请求对话框。
类型 :(readOnly?: boolean) => Promise<'authorized' | 'denied' | 'restricted' | 'undetermined'>
参数:
readOnly: 是否只检查读取权限,默认为false
使用场景:
- 在执行日历操作前检查权限
- 根据权限状态显示不同的 UI
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const checkPermission = async () => {
const status = await RNCalendarEvents.checkPermissions();
console.log('当前权限状态:', status);
return status === 'authorized';
};
findCalendars - 获取所有日历
获取设备上所有可用的日历列表。
类型 :() => Promise<Calendar[]>
返回值:日历对象数组,包含以下属性:
| 属性 | 类型 | 说明 |
|---|---|---|
| id | string | 日历唯一标识 |
| title | string | 日历标题 |
| source | object | 日历来源 |
| color | string | 日历颜色 |
| allowsModifications | boolean | 是否允许修改 |
| type | string | 日历类型 |
使用场景:
- 显示日历列表供用户选择
- 获取默认日历 ID
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const getCalendars = async () => {
const calendars = await RNCalendarEvents.findCalendars();
calendars.forEach((calendar) => {
console.log('日历:', calendar.title, 'ID:', calendar.id);
});
return calendars;
};
saveCalendar - 创建日历
创建一个新的日历。
类型 :(options: CalendarOptions) => Promise<string>
参数:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 是 | 日历标题 |
| type | string | 否 | 日历类型 |
| displayName | string | 否 | 显示名称 |
| color | string | 否 | 日历颜色 |
返回值:新创建日历的 ID
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const createCalendar = async () => {
const calendarId = await RNCalendarEvents.saveCalendar({
title: '我的日历',
type: 'local',
displayName: '个人日程',
color: '#FF5722',
});
console.log('创建日历成功, ID:', calendarId);
return calendarId;
};
removeCalendar - 删除日历
根据 ID 删除指定的日历。
类型 :(id: string) => Promise<boolean>
参数:
id: 日历 ID
返回值:是否删除成功
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const deleteCalendar = async (calendarId: string) => {
const success = await RNCalendarEvents.removeCalendar(calendarId);
console.log('删除日历:', success ? '成功' : '失败');
return success;
};
findEventById - 根据 ID 查询事件
根据事件 ID 获取单个事件的详细信息。
类型 :(id: string) => Promise<Event | null>
参数:
id: 事件 ID
返回值 :事件对象,如果未找到则返回 null
事件对象属性:
| 属性 | 类型 | 说明 |
|---|---|---|
| id | string | 事件唯一标识 |
| title | string | 事件标题 |
| location | string | 事件地点 |
| startDate | string | 开始时间 |
| endDate | string | 结束时间 |
| allDay | boolean | 是否全天事件 |
| description | string | 事件描述 |
| calendarId | string | 所属日历 ID |
| attendees | array | 参与者列表 |
| recurrenceRule | object | 重复规则 |
| reminders | array | 提醒列表 |
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const getEventById = async (eventId: string) => {
const event = await RNCalendarEvents.findEventById(eventId);
if (event) {
console.log('事件标题:', event.title);
console.log('开始时间:', event.startDate);
}
return event;
};
fetchAllEvents - 获取所有事件
获取指定时间范围内的所有事件。
类型 :(startDate: string, endDate: string, calendars?: string[]) => Promise<Event[]>
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| startDate | string | 是 | 开始时间(时间戳字符串) |
| endDate | string | 是 | 结束时间(时间戳字符串) |
| calendars | string[] | 否 | 要查询的日历 ID 列表 |
使用场景:
- 显示日程列表
- 日历视图数据源
- 查询特定时间段的事件
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const getEvents = async () => {
const now = Date.now();
const startDate = now.toString();
const endDate = (now + 7 * 24 * 60 * 60 * 1000).toString();
const events = await RNCalendarEvents.fetchAllEvents(startDate, endDate);
events.forEach((event) => {
console.log('事件:', event.title, '开始:', event.startDate);
});
return events;
};
saveEvent - 创建/更新事件
创建新事件或更新已有事件。如果 details 中包含 id,则更新该事件;否则创建新事件。
类型 :(title: string, details?: EventDetails, options?: SaveEventOptions) => Promise<string>
参数:
title: 事件标题
details: 事件详情
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 否 | 事件 ID(更新时) |
| type | number | 否 | 事件类型 |
| title | string | 是 | 事件标题 |
| location | object | 否 | 地点信息 |
| startTime | number | 是 | 开始时间(毫秒) |
| endTime | number | 是 | 结束时间(毫秒) |
| isAllDay | boolean | 否 | 是否全天事件 |
| attendee | array | 否 | 参与者列表 |
| timeZone | string | 否 | 时区 |
| reminderTime | number[] | 否 | 提醒时间列表 |
| recurrenceRule | object | 否 | 重复规则 |
| description | string | 否 | 事件描述 |
location 对象:
| 属性 | 类型 | 说明 |
|---|---|---|
| location | string | 地点名称 |
| longitude | number | 经度 |
| latitude | number | 纬度 |
recurrenceRule 对象:
| 属性 | 类型 | 说明 |
|---|---|---|
| recurrenceFrequency | number | 重复频率(1=每日,2=每周等) |
| expire | number | 过期时间(0表示永不过期) |
返回值:事件的 ID
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const createEvent = async () => {
const now = Date.now();
const eventId = await RNCalendarEvents.saveEvent('项目会议', {
title: '项目会议',
startTime: now + 60 * 60 * 1000,
endTime: now + 2 * 60 * 60 * 1000,
location: {
location: '会议室A',
longitude: 116.397,
latitude: 39.908,
},
description: '讨论项目进度',
isAllDay: false,
reminderTime: [now + 30 * 60 * 1000],
});
console.log('创建事件成功, ID:', eventId);
return eventId;
};
const updateEvent = async (eventId: string) => {
const now = Date.now();
const newEventId = await RNCalendarEvents.saveEvent('项目会议(已更新)', {
id: eventId,
title: '项目会议(已更新)',
startTime: now + 2 * 60 * 60 * 1000,
endTime: now + 3 * 60 * 60 * 1000,
description: '会议时间已调整',
});
console.log('更新事件成功');
return newEventId;
};
removeEvent - 删除事件
根据 ID 删除指定的事件。
类型 :(id: string, options?: { futureEvents?: boolean }) => Promise<boolean>
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 事件 ID |
| futureEvents | boolean | 否 | 是否删除未来的重复事件 |
返回值:是否删除成功
tsx
import RNCalendarEvents from 'react-native-calendar-events';
const deleteEvent = async (eventId: string) => {
const success = await RNCalendarEvents.removeEvent(eventId);
console.log('删除事件:', success ? '成功' : '失败');
return success;
};
📋 完整示例

ts
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
SafeAreaView,
StatusBar,
Alert,
TextInput,
Switch,
TouchableWithoutFeedback,
} from 'react-native';
import RNCalendarEvents from 'react-native-calendar-events';
interface Calendar {
id: string;
title: string;
type?: string;
displayName?: string;
}
interface Event {
id: string;
title: string;
startDate: string;
endDate?: string;
location?: string;
description?: string;
allDay?: boolean;
}
const App: React.FC = () => {
const [calendars, setCalendars] = useState<Calendar[]>([]);
const [events, setEvents] = useState<Event[]>([]);
const [isAuthorized, setIsAuthorized] = useState(false);
const [showForm, setShowForm] = useState(false);
const [eventTitle, setEventTitle] = useState('');
const [eventDescription, setEventDescription] = useState('');
const [eventLocation, setEventLocation] = useState('');
const [isAllDay, setIsAllDay] = useState(false);
useEffect(() => {
checkPermission();
}, []);
const checkPermission = async () => {
const status = await RNCalendarEvents.checkPermissions();
setIsAuthorized(status === 'authorized');
if (status === 'authorized') {
loadCalendars();
loadEvents();
}
};
const requestPermission = async () => {
const status = await RNCalendarEvents.requestPermissions();
setIsAuthorized(status === 'authorized');
if (status === 'authorized') {
loadCalendars();
loadEvents();
} else {
Alert.alert('权限被拒绝', '请在设置中允许访问日历');
}
};
const loadCalendars = async () => {
try {
const calendarList = await RNCalendarEvents.findCalendars();
setCalendars(calendarList);
} catch (error) {
console.error('获取日历失败:', error);
}
};
const loadEvents = async () => {
try {
const now = new Date();
const startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();
const endDate = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString();
const eventList = await RNCalendarEvents.fetchAllEvents(startDate, endDate);
setEvents(eventList);
} catch (error) {
console.error('获取事件失败:', error);
}
};
const createEvent = async () => {
if (!eventTitle.trim()) {
Alert.alert('提示', '请输入事件标题');
return;
}
try {
const now = new Date();
let startDate: string;
let endDate: string;
if (isAllDay) {
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
startDate = today.toISOString();
endDate = tomorrow.toISOString();
} else {
startDate = new Date(now.getTime() + 60 * 60 * 1000).toISOString();
endDate = new Date(now.getTime() + 2 * 60 * 60 * 1000).toISOString();
}
const eventId = await RNCalendarEvents.saveEvent(eventTitle, {
startDate,
endDate,
location: eventLocation || undefined,
description: eventDescription,
allDay: isAllDay,
});
Alert.alert('成功', '事件创建成功');
setShowForm(false);
setEventTitle('');
setEventDescription('');
setEventLocation('');
setIsAllDay(false);
loadEvents();
} catch (error) {
console.error('创建事件失败:', error);
Alert.alert('错误', '创建事件失败');
}
};
const deleteEvent = async (eventId: string) => {
Alert.alert('确认删除', '确定要删除这个事件吗?', [
{ text: '取消', style: 'cancel' },
{
text: '删除',
style: 'destructive',
onPress: async () => {
try {
await RNCalendarEvents.removeEvent(eventId);
Alert.alert('成功', '事件已删除');
loadEvents();
} catch (error) {
console.error('删除事件失败:', error);
Alert.alert('错误', '删除事件失败');
}
},
},
]);
};
const formatDate = (dateString: string) => {
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return dateString;
}
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
const day = String(date.getUTCDate()).padStart(2, '0');
const hours = String(date.getUTCHours()).padStart(2, '0');
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
} catch {
return dateString;
}
};
const getLocationText = (location: Event['location']) => {
return location || '';
};
if (!isAuthorized) {
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
<View style={styles.permissionContainer}>
<Text style={styles.permissionTitle}>日历权限</Text>
<Text style={styles.permissionText}>
需要日历权限才能管理您的日程事件
</Text>
<TouchableOpacity style={styles.permissionButton} onPress={requestPermission}>
<Text style={styles.permissionButtonText}>授权访问日历</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
<View style={styles.header}>
<Text style={styles.headerTitle}>日历事件管理</Text>
<TouchableOpacity
style={styles.addButton}
onPress={() => setShowForm(true)}
>
<Text style={styles.addButtonText}>+ 新建</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.content}>
<View style={styles.section}>
<Text style={styles.sectionTitle}>日历列表 ({calendars.length})</Text>
{calendars.length === 0 ? (
<Text style={styles.emptyText}>暂无日历</Text>
) : (
calendars.map((calendar, index) => (
<View key={calendar.id || index} style={styles.calendarItem}>
<View style={styles.calendarColor} />
<View style={styles.calendarInfo}>
<Text style={styles.calendarTitle}>{calendar.title || '未命名日历'}</Text>
<Text style={styles.calendarType}>类型: {calendar.type || '本地'}</Text>
</View>
</View>
))
)}
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>近期事件 ({events.length})</Text>
{events.length === 0 ? (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}>暂无事件</Text>
<Text style={styles.emptyHint}>点击右上角"新建"创建事件</Text>
</View>
) : (
events.map((event) => (
<View key={event.id} style={styles.eventItem}>
<View style={styles.eventHeader}>
<Text style={styles.eventTitle}>{event.title}</Text>
<TouchableOpacity onPress={() => deleteEvent(event.id)}>
<Text style={styles.deleteText}>删除</Text>
</TouchableOpacity>
</View>
<Text style={styles.eventTime}>开始: {formatDate(event.startDate)}</Text>
{event.endDate && (
<Text style={styles.eventTime}>结束: {formatDate(event.endDate)}</Text>
)}
{event.location && (
<Text style={styles.eventLocation}>📍 {getLocationText(event.location)}</Text>
)}
{event.description && (
<Text style={styles.eventDescription}>{event.description}</Text>
)}
</View>
))
)}
</View>
</ScrollView>
{showForm && (
<TouchableWithoutFeedback onPress={() => setShowForm(false)}>
<View style={styles.overlay}>
<TouchableWithoutFeedback onPress={() => {}}>
<View style={styles.formContainer}>
<Text style={styles.formTitle}>新建事件</Text>
<Text style={styles.inputLabel}>事件标题</Text>
<TextInput
style={styles.input}
placeholder="请输入事件标题"
value={eventTitle}
onChangeText={setEventTitle}
/>
<Text style={styles.inputLabel}>地点(可选)</Text>
<TextInput
style={styles.input}
placeholder="请输入地点"
value={eventLocation}
onChangeText={setEventLocation}
/>
<Text style={styles.inputLabel}>描述(可选)</Text>
<TextInput
style={[styles.input, styles.textArea]}
placeholder="请输入描述"
value={eventDescription}
onChangeText={setEventDescription}
multiline
/>
<View style={styles.switchRow}>
<Text style={styles.switchLabel}>全天事件</Text>
<Switch value={isAllDay} onValueChange={setIsAllDay} />
</View>
<View style={styles.formButtons}>
<TouchableOpacity
style={[styles.formButton, styles.cancelButton]}
onPress={() => setShowForm(false)}
>
<Text style={styles.cancelButtonText}>取消</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.formButton, styles.confirmButton]}
onPress={createEvent}
>
<Text style={styles.confirmButtonText}>创建</Text>
</TouchableOpacity>
</View>
</View>
</TouchableWithoutFeedback>
</View>
</TouchableWithoutFeedback>
)}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F5F5',
},
permissionContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
permissionTitle: {
fontSize: 24,
fontWeight: '700',
color: '#333333',
marginBottom: 12,
},
permissionText: {
fontSize: 16,
color: '#666666',
textAlign: 'center',
marginBottom: 24,
},
permissionButton: {
backgroundColor: '#007AFF',
paddingHorizontal: 32,
paddingVertical: 14,
borderRadius: 8,
},
permissionButtonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: '600',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 16,
backgroundColor: '#FFFFFF',
borderBottomWidth: 1,
borderBottomColor: '#E5E5EA',
},
headerTitle: {
fontSize: 20,
fontWeight: '700',
color: '#333333',
},
addButton: {
backgroundColor: '#007AFF',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 6,
},
addButtonText: {
color: '#FFFFFF',
fontSize: 14,
fontWeight: '600',
},
content: {
flex: 1,
padding: 16,
},
section: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
padding: 16,
marginBottom: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
color: '#333333',
marginBottom: 12,
},
calendarItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
calendarColor: {
width: 12,
height: 12,
borderRadius: 6,
backgroundColor: '#007AFF',
marginRight: 12,
},
calendarInfo: {
flex: 1,
},
calendarTitle: {
fontSize: 16,
color: '#333333',
fontWeight: '500',
},
calendarType: {
fontSize: 14,
color: '#999999',
marginTop: 2,
},
emptyContainer: {
alignItems: 'center',
paddingVertical: 32,
},
emptyText: {
fontSize: 16,
color: '#999999',
},
emptyHint: {
fontSize: 14,
color: '#CCCCCC',
marginTop: 8,
},
eventItem: {
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
eventHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
eventTitle: {
fontSize: 16,
fontWeight: '600',
color: '#333333',
flex: 1,
},
deleteText: {
fontSize: 14,
color: '#FF3B30',
marginLeft: 12,
},
eventTime: {
fontSize: 14,
color: '#666666',
marginTop: 4,
},
eventLocation: {
fontSize: 14,
color: '#666666',
marginTop: 4,
},
eventDescription: {
fontSize: 14,
color: '#999999',
marginTop: 4,
},
overlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'center',
alignItems: 'center',
},
formContainer: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
padding: 20,
width: '90%',
maxWidth: 400,
},
formTitle: {
fontSize: 20,
fontWeight: '700',
color: '#333333',
marginBottom: 20,
textAlign: 'center',
},
inputLabel: {
fontSize: 14,
color: '#666666',
marginBottom: 4,
},
input: {
borderWidth: 1,
borderColor: '#E5E5EA',
borderRadius: 8,
paddingHorizontal: 16,
paddingVertical: 12,
fontSize: 16,
marginBottom: 12,
},
textArea: {
height: 80,
textAlignVertical: 'top',
},
switchRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 8,
marginBottom: 20,
},
switchLabel: {
fontSize: 16,
color: '#333333',
},
formButtons: {
flexDirection: 'row',
gap: 12,
},
formButton: {
flex: 1,
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
},
cancelButton: {
backgroundColor: '#F0F0F0',
},
cancelButtonText: {
fontSize: 16,
color: '#666666',
fontWeight: '600',
},
confirmButton: {
backgroundColor: '#007AFF',
},
confirmButtonText: {
fontSize: 16,
color: '#FFFFFF',
fontWeight: '600',
},
});
export default App;
⚠️ 注意事项
1. 权限配置
- 必须在
module.json5中配置READ_CALENDAR和WRITE_CALENDAR权限 - 权限说明文案需要在
string.json中定义 - 首次使用时需要调用
requestPermissions()请求权限
2. 时间格式
fetchAllEvents的参数需要使用时间戳字符串saveEvent的startTime和endTime使用毫秒时间戳- 时间戳需要转换为字符串传递
3. 日历 ID
- 创建事件前需要确保有可用的日历
- 可以通过
findCalendars()获取日历列表 - 建议使用系统默认日历或创建专用日历
4. 事件更新
- 更新事件时需要在
details中传入事件 ID - 更新会覆盖原有的事件信息
- 建议先查询事件详情再进行更新
5. 重复事件
recurrenceFrequency值:1=每日,2=每周,3=每月,4=每年expire为 0 表示永不过期- 删除重复事件时可以指定
futureEvents参数
6. 错误处理
- 所有 API 都返回 Promise,需要使用 try-catch 捕获错误
- 权限被拒绝时需要引导用户到设置中开启
- 日历操作失败时需要给用户明确的错误提示
7. 性能优化
- 查询事件时指定合理的时间范围
- 避免频繁查询大量事件
- 建议缓存日历列表减少查询次数
