实现在h5中添加日历提醒:safari唤起系统日历,其它浏览器跳转google日历

**需求:**点击按钮后,将设定的一些信息插入到系统日历的日程安排中。

调研过程

  1. 先google了一段时间,了解该需求大概的实现方式。可以创建日历文件,在点击的时候下载该日历文件,看起来还比较复杂,并且由于不具备系统日历相关的知识,担心纯手写造成的bug难以解决,所以放弃了。
  2. 在npm上搜索了相关的插件,发现了add-to-calendar-button, 看文档发现适配了很多平台上的日历,但是真正使用后发现无法满足需求,这个插件不支持配置日程中url。并且在第三方软件中也出现了打不开的bug,所以放弃了这个插件。但是在调试的过程中发现在android设备上或ios非safari的浏览器是无法直接插入系统日历的,而是下载日历ics文件自行导入。
  3. 既然知道了不同系统以及浏览器中的效果都不一样,那就只能想一个满足需求的同时还提高用户体验的办法了。我的项目是海外的,所以用safari和google浏览器的用户居多,并且也了解到需求方主要是要用ios日历提醒。
  4. 主动和产品沟通,ios用户保障在safari浏览器中插入系统日历,除开safari以外的用户(包含android/其它浏览器/第三方app内置浏览器)点击按钮后跳转google日历。
  5. 又去npm找生成ics文件的插件,发现了ics.js,配置项挺多的也满足需求。google日历官方给出的方式需要注册appKey,但是ai告诉我有另一种方式:跳转url并传参,但是google日历找不到参数文档,不过主要的信息也都可以写入。

源代码

封装公共方法,点击按钮后调用addCalendar

javascript 复制代码
import moment from 'moment';
import { createEvent } from 'ics';
import { getBrowser } from '@/utils/common';

/**
 * 添加谷歌日历
 * @param {Object} event - 日历事件对象
*/
const addGoogleCalendar = (event) => {
  const startDate = moment(event.start).format('YYYYMMDD[T]HHmmss');
  const endDate = moment(event.end).format('YYYYMMDD[T]HHmmss');

  const params = new URLSearchParams({
    action: 'TEMPLATE',
    text: event.name,
    dates: `${startDate}/${endDate}`,
    details: `${event.url}\n${event.description}`,
    ctz: event.timezone || 'Asia/Tokyo'
  });

  const googleUrl = `https://calendar.google.com/calendar/render?${params.toString()}`;
  console.log(decodeURIComponent(googleUrl));

  window.open(googleUrl, '_blank');
}


/**
 * 为 Safari 用户创建并下载 ICS 文件
 * @param {Object} event - 日历事件对象
*/
const createICSEvent = (event) => {
  const filename = `${event.name}.ics`;

  const startMoment = moment.utc(event.start).local();
  const endMoment = moment.utc(event.end).local();

  const icsEvent = {
    start: [
      startMoment.year(),
      startMoment.month()+1,
      startMoment.date(),
      startMoment.hour(),
      startMoment.minute()
    ],
    end: [
      endMoment.year(),
      endMoment.month()+1,
      endMoment.date(),
      endMoment.hour(),
      endMoment.minute()
    ],
    title: event.name,
    description: `${event.url}\n${event.description}`,
    url: event.url,
    alarms: [{
      action: 'display',
      description: 'Reminder',
      trigger: { ...event.reminder, before: true }
    }]
  };

  createEvent(icsEvent, (error, value) => {
    if (error) {
      console.log(error);
      return;
    }
    if (!value) return;

    const file = new File([value], filename, { type: 'text/calendar' });
    const link = document.createElement('a');
    const url = URL.createObjectURL(file);

    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(url);
  });
}

/**
 * 添加日历
 * @param {Object} params - 日历参数
 * @param {string} params.name - 日历事件名称
 * @param {number} params.start - 日历事件开始时间
 * @param {number} params.end - 日历事件结束时间
 * @param {string} params.url - 日历事件 URL
 * @param {string} params.description - 日历事件描述(备注)
*/
export const addCalendar = (params) => {
  const finalParams = {
    ...params,
    timezone: 'Asia/Tokyo',
    reminder: { hours: 1 }
  }

  const { isSafari } = getBrowser();

  if (isSafari) {
    createICSEvent(finalParams);
  } else {
    addGoogleCalendar(finalParams);
  }
}

相关文档

  1. stack overflow上关于google日历参数的回答
  2. ics.js
  3. add-to-calendar-button
相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax