更新记录

1.1.0(2026-02-03)

  • 新增 Android 平台支持
  • 使用 Android CalendarContract API 实现日历提醒功能
  • 自动请求日历读写权限
  • 统一 iOS 和 Android 返回数据格式

平台兼容性

uni-app(4.84)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × × × × × 5.0 12 ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - × ×

uni-app x(4.84)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × 5.0 12 × ×

jun-calendar-event

uni-app 系统日历提醒插件 - 支持 iOS & Android 双平台

一个功能完善的 UTS 插件,用于在 uni-app 应用中调用系统原生日历功能,支持创建、查询、删除日历提醒事件。

✨ 功能特性

  • 双平台支持: iOS 和 Android 原生实现
  • 完整功能: 创建、查询、删除日历事件
  • 重复提醒: 支持每周重复规则
  • 多时间提醒: 可设置多个提醒时间点
  • 全天事件: 支持全天事件类型
  • 权限管理: 自动请求和处理日历权限
  • 时区适配: 自动使用设备当前时区

📱 平台实现

平台 实现方式 最低版本 状态
iOS EventKit Framework iOS 9.0+ ✅ 已支持
Android CalendarContract API Android 5.0+ (API 21) ✅ 已支持

🚀 快速开始

1. 安装插件

将插件导入到项目的 uni_modules 目录下,或通过 HBuilderX 插件市场导入。

2. 导入使用

import {
    createEventCalendar,
    removeEventCalendar,
    getEventCalendar,
    removeAllEventCalendar,
} from "@/uni_modules/jun-calendar-event";

3. 创建日历提醒

createEventCalendar({
    title: "团队会议",
    notes: "讨论项目进度",
    startDate: "2024-12-25 10:00:00",
    endDate: "2024-12-25 12:00:00",
    alarmOffset: [-60 * 15], // 提前15分钟提醒
    isAllDay: false,
    weeks: [], // 不重复
    success: (res) => {
        console.log("创建成功,事件ID:", res.eventId);
        uni.showToast({ title: "提醒已创建", icon: "success" });
    },
    fail: (err) => {
        console.log("创建失败:", err.message);
        uni.showToast({ title: "创建失败", icon: "none" });
    },
});

4. 查询日历提醒

getEventCalendar((eventsJson) => {
    const events = JSON.parse(eventsJson);
    console.log("查询到的事件:", events);
    // events 是一个数组,包含所有日历事件
});

5. 删除日历提醒

removeEventCalendar({
    idfer: "event_id_here", // 从查询结果中获取的事件ID
    success: (res) => {
        console.log("删除成功");
        uni.showToast({ title: "已删除", icon: "success" });
    },
    fail: (err) => {
        console.log("删除失败:", err.message);
    },
});

📖 API 文档

createEventCalendar(options)

创建日历提醒事件

参数说明

参数 类型 必填 说明
title String 日历事件标题
notes String 日历事件备注/描述
startDate String 开始时间,格式:yyyy-MM-dd HH:mm:ss
endDate String 结束时间,格式:yyyy-MM-dd HH:mm:ss
alarmOffset Array\<Number> 提醒时间数组,单位:秒,负数表示提前。例如:[-60*15] 表示提前 15 分钟
isAllDay Boolean 是否为全天事件
weeks Array\<Number> 重复规则,[0,1,2,3,4,5,6] 分别代表周日到周六,空数组表示不重复
success Function 成功回调,参数:{ eventId: string }
fail Function 失败回调,参数:{ message: string }

示例

// 创建每周一、三、五的健身提醒
createEventCalendar({
    title: "健身训练",
    notes: "记得带运动装备",
    startDate: "2024-12-23 18:00:00",
    endDate: "2024-12-23 20:00:00",
    alarmOffset: [-60 * 30, -60 * 10], // 提前30分钟和10分钟提醒
    isAllDay: false,
    weeks: [1, 3, 5], // 周一、周三、周五
    success: (res) => console.log("创建成功", res.eventId),
    fail: (err) => console.log("创建失败", err.message),
});

// 创建全天生日提醒
createEventCalendar({
    title: "小明生日",
    notes: "记得准备礼物",
    startDate: "2024-12-25 00:00:00",
    endDate: "2024-12-25 23:59:59",
    alarmOffset: [-60 * 60 * 24 * 7, -60 * 60 * 24], // 提前7天和1天
    isAllDay: true,
    weeks: [],
    success: (res) => console.log("创建成功"),
    fail: (err) => console.log("创建失败"),
});

getEventCalendar(callback)

查询日历提醒列表(查询当前时间到未来一年内的事件)

参数说明

参数 类型 必填 说明
callback Function 回调函数,参数为 JSON 字符串

返回数据格式

[
    {
        eventId: "事件唯一标识",
        title: "事件标题",
        notes: "事件备注",
        startDate: "2024-12-25 10:00:00",
        endDate: "2024-12-25 12:00:00",
        rules: [1, 3, 5], // 重复规则,空数组表示不重复
    },
];

示例

getEventCalendar((eventsJson) => {
    const events = JSON.parse(eventsJson);
    console.log("查询到 " + events.length + " 个事件");

    events.forEach((event) => {
        console.log("标题:", event.title);
        console.log("开始时间:", event.startDate);
        console.log("重复规则:", event.rules);
    });
});

removeEventCalendar(options)

删除指定的日历提醒

参数说明

参数 类型 必填 说明
idfer String 事件 ID(从查询结果中获取)
success Function 成功回调,参数:{ message: string }
fail Function 失败回调,参数:{ message: string }

示例

removeEventCalendar({
    idfer: "BDAF720F-E341-46F8-851F-FE7F5A5DFD10",
    success: (res) => {
        console.log("删除成功");
        uni.showToast({ title: "已删除", icon: "success" });
    },
    fail: (err) => {
        console.log("删除失败:", err.message);
        uni.showToast({ title: "删除失败", icon: "none" });
    },
});

removeAllEventCalendar(options)

删除指定时间段内的所有日历提醒

参数说明

参数 类型 必填 说明
startDate String 开始时间,格式:yyyy-MM-dd HH:mm:ss
endDate String 结束时间,格式:yyyy-MM-dd HH:mm:ss
success Function 成功回调
fail Function 失败回调

示例

// 删除2024年12月的所有提醒
removeAllEventCalendar({
    startDate: "2024-12-01 00:00:00",
    endDate: "2024-12-31 23:59:59",
    success: (res) => console.log("批量删除成功"),
    fail: (err) => console.log("删除失败"),
});

💡 完整示例

<template>
    <view class="container">
        <view class="header">
            <text class="title">日历提醒管理</text>
        </view>

        <view class="actions">
            <button @click="createReminder" type="primary">创建提醒</button>
            <button @click="loadReminders" type="default">刷新列表</button>
        </view>

        <view class="list">
            <view v-for="item in reminderList" :key="item.eventId" class="list-item">
                <view class="item-title">{{ item.title }}</view>
                <view class="item-time">{{ item.startDate }}</view>
                <view class="item-notes">{{ item.notes }}</view>
                <button @click="deleteReminder(item.eventId)" size="mini" type="warn">
                    删除
                </button>
            </view>
        </view>

        <view v-if="reminderList.length === 0" class="empty">
            <text>暂无日历提醒</text>
        </view>
    </view>
</template>

<script>
import {
    createEventCalendar,
    removeEventCalendar,
    getEventCalendar,
} from "@/uni_modules/jun-calendar-event";

export default {
    data() {
        return {
            reminderList: [],
        };
    },

    onLoad() {
        this.loadReminders();
    },

    methods: {
        // 创建提醒
        createReminder() {
            const tomorrow = new Date();
            tomorrow.setDate(tomorrow.getDate() + 1);
            tomorrow.setHours(10, 0, 0, 0);

            const start = this.formatDate(tomorrow);
            tomorrow.setHours(12, 0, 0, 0);
            const end = this.formatDate(tomorrow);

            createEventCalendar({
                title: `会议提醒_${Math.floor(Math.random() * 100)}`,
                notes: "这是一个测试提醒",
                startDate: start,
                endDate: end,
                alarmOffset: [-60 * 15], // 提前15分钟
                isAllDay: false,
                weeks: [],
                success: (res) => {
                    uni.showToast({ title: "创建成功", icon: "success" });
                    this.loadReminders();
                },
                fail: (err) => {
                    uni.showToast({
                        title: "创建失败: " + err.message,
                        icon: "none",
                    });
                },
            });
        },

        // 加载提醒列表
        loadReminders() {
            getEventCalendar((data) => {
                this.reminderList = JSON.parse(data);
            });
        },

        // 删除提醒
        deleteReminder(eventId) {
            removeEventCalendar({
                idfer: eventId,
                success: () => {
                    uni.showToast({ title: "删除成功", icon: "success" });
                    this.loadReminders();
                },
                fail: (err) => {
                    uni.showToast({
                        title: "删除失败: " + err.message,
                        icon: "none",
                    });
                },
            });
        },

        // 格式化日期
        formatDate(date) {
            const pad = (n) => String(n).padStart(2, "0");
            return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(
                date.getDate()
            )} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(
                date.getSeconds()
            )}`;
        },
    },
};
</script>

<style scoped>
.container {
    padding: 20rpx;
    background-color: #f5f5f5;
    min-height: 100vh;
}

.header {
    text-align: center;
    padding: 20rpx;
}

.title {
    font-size: 36rpx;
    font-weight: bold;
}

.actions {
    padding: 20rpx 0;
}

.actions button {
    margin: 10rpx 0;
}

.list {
    margin-top: 20rpx;
}

.list-item {
    background-color: white;
    padding: 20rpx;
    margin-bottom: 20rpx;
    border-radius: 10rpx;
}

.item-title {
    font-size: 32rpx;
    font-weight: bold;
    margin-bottom: 10rpx;
}

.item-time {
    font-size: 28rpx;
    color: #666;
    margin-bottom: 10rpx;
}

.item-notes {
    font-size: 26rpx;
    color: #999;
    margin-bottom: 10rpx;
}

.empty {
    text-align: center;
    color: #999;
    margin-top: 100rpx;
}
</style>

⚠️ 注意事项

1. 自定义基座

UTS 插件必须使用自定义基座进行测试和运行

在 HBuilderX 中:

  1. 点击 运行 -> 运行到手机或模拟器 -> 制作自定义调试基座
  2. 等待云端打包完成
  3. 使用自定义基座运行项目

2. 日期格式

日期时间必须严格遵守格式:yyyy-MM-dd HH:mm:ss

✅ 正确:2024-12-25 10:00:00
❌ 错误:2024/12/25 10:00:00
❌ 错误:2024-12-25

3. 提醒时间

alarmOffset 参数说明:

  • 单位:秒
  • 负数表示提前,正数表示延后
  • 示例:
    • -60 * 15 = 提前 15 分钟
    • -60 * 60 = 提前 1 小时
    • -60 * 60 * 24 = 提前 1 天

4. 重复规则

weeks 参数说明:

  • [] - 不重复
  • [0] - 每周日
  • [1] - 每周一
  • [1, 3, 5] - 每周一、三、五
  • [0, 1, 2, 3, 4, 5, 6] - 每天

5. 权限处理

  • iOS: 首次使用会弹出系统权限请求对话框
  • Android: 自动请求日历读写权限,用户拒绝后可引导到设置页面

将额外权限加入到 manifest.json 》app-plus 》 distribute 》 android 》 permissions

<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>

6. 错误处理

常见错误及解决方案:

错误信息 原因 解决方案
repeat 相同时间已存在事件 修改时间或删除旧事件
需要日历权限 用户未授权 引导用户到设置中开启权限
创建事件失败 系统错误 检查日期格式和参数

🔍 常见问题

Q: 为什么创建失败?
A: 请检查:

  1. 是否使用了自定义基座
  2. 日期格式是否正确
  3. 是否授予了日历权限

Q: 查询不到刚创建的事件?
A: 查询范围是当前时间到未来一年,如果事件时间已过,则不会显示。

Q: 如何在系统日历中查看?
A:

  • iOS: 打开系统"日历"应用
  • Android: 打开"日历"或"Google 日历"应用

Q: 删除后系统日历还能看到?
A: 可能是缓存问题,重启日历应用或等待几秒刷新。

📚 相关文档

📝 更新日志

v1.1.0 (2024-12-20)

  • ✨ 新增 Android 平台完整支持
  • 🔧 统一 iOS 和 Android 数据格式
  • 📝 完善文档和示例
  • 🐛 修复已知问题

v1.0.0 (2024-07-04)

  • 🎉 首次发布
  • ✅ iOS 平台支持

📄 许可证

MIT License

💬 技术支持

如有问题或建议,欢迎反馈。


开发愉快! 🎉

隐私、权限声明

1. 本插件需要申请的系统权限列表:

日历读写

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。