FullCalendar日历组件集成实战(20)

背景

有一些应用系统或应用功能,如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件,但功能比较简单,用来做数据展现勉强可用。但如果需要进行复杂的数据展示,以及互动操作如通过点击添加事件,则需要做大量的二次开发。
FullCalendar是一款备受欢迎的开源日历组件,以其强大的功能而著称。其基础功能不仅免费且开源,为开发者提供了极大的便利,仅有少量高级功能需要收费。然而,尽管该组件功能卓越,其文档却相对简洁,导致在集成过程中需要开发者自行摸索与探索,这无疑增加了不少学习和验证的时间成本。
为此,本专栏通过日程管理系统的真实案例,手把手带你了解该组件的属性和功能,通过需求导向的方式,详细阐述FullCalendar组件的集成思路和实用解决方案。
在介绍过程中,我们将重点关注集成要点和注意事项,力求帮助开发者在集成过程中少走弯路,提供有效的避坑指南,从而提升开发效率,更好地利用这款优秀的日历组件。

官网:https://fullcalendar.io/
image.png
环境Vue3+Element Plus+FullCalendar 6.1.11。

使用

重构任务处理为无刷新模式

前面我们在日历视图上进行任务(事件)的增删改,后端持久化后,前端都调用了refresh刷新方法,重新从后端调用数据,从用户体验上,有一个肉眼可见的页面刷新和数据加载过程。
接下来,我们通过调用FullCalendar的api,来实现任务的无刷新模式。

新增任务

在日历视图中点击或选择一个时间范围后,自动弹出创建任务对话框,如下图所示:
image.png
点击保存按钮后,使用emit触发父页面,也就是FullCalendar所在页面的refresh方法,如下所示:

<AddPage ref="addPage" @refresh="refresh" />

// 刷新
refresh() {
  const fullCalendar = this.$refs.fullCalendar.calendar

  let query = this.$route.query
  query = Object.assign(query, {
    viewType: fullCalendar.view.type,
    showAllFlag: this.showAllFlag,
    start: this.$dateFormatter.formatUTCDate(fullCalendar.view.activeStart),
    end: this.$dateFormatter.formatUTCDate(fullCalendar.view.activeEnd)
  })
  refreshSelectedTagWithQuery(query)
}

对于新增任务,FullCalendar提供了addEvent方法,重构如下:

<AddPage ref="addPage" @refresh="addTask" />

// 新增任务
addTask(task) {
  // 获取日历对象
  const fullCalendar = this.$refs.fullCalendar.calendar
  // 将任务数据转换为日历事件
  const event = this.convertTaskToEvent(task)
  // 调用api添加任务
  fullCalendar.addEvent(event)
}

// 任务数据转换为事件对象
convertTaskToEvent(task) {
  // 计算全天事件属性值
  const allDay = this.calculateAllDay(task.startTime, task.endTime)
  // 数据转换
  return {
    id: task.id,
    title: task.name,
    start: task.startTime,
    end: task.endTime,
    allDay: allDay,
    extendedProps: {
      status: task.status,
      plannedDuration: task.plannedDuration
    }
  }
},
 // 计算全天事件属性值
calculateAllDay(startTime, endTime) {     
  let allDay = false
   // 若起止时间不为空且均为00:00:00,则设置为allDay属性为true
  if (
    startTime &&
    endTime &&
    startTime.substr(11, 8) === '00:00:00' &&
    endTime.substr(11, 8) === '00:00:00'
  ) {
    allDay = true
  }
  return allDay
}  

任务数据转换为事件对象的方法,以及计算全天事件属性值的方法,因为多处使用,都是从原已实现的方法中通过重构提取出来的。

通过以上重构处理,实现了任务无刷新添加。

删除任务

在日历视图中右键一个现有任务后,弹出菜单中可以选择“删除”,如下所示:
image.png
原处理逻辑如下:

// 事件右键菜单命令
eventContextMenuSelect(command) {
  const id = this.contextMenuEventId
  if (command === 'copy') {
    this.$api.personaltask.task.addSingleByCopy(id).then((res) => {
      this.$refs.modifyPage.init(res.data.id)
    })
  } else if (command === 'remove') {
    this.$confirm('此操作将移除任务, 是否继续?', '确认', {
      type: 'warning'
    })
      .then(() => {
        this.$api.personaltask.task.remove(id).then(() => {
          this.refresh()
        })
      })
      .catch(() => {
        this.$message.info('已取消')
      })
  } else if (command === 'addLog') {
    this.addLog(id)
  } else if (command === 'setCompleted') {
    this.setCompleted(id)
  } else if (command === 'setPending') {
    this.setPending(id)
  }
  // 隐藏右键菜单
  this.eventContextMenu.visible = false
}

若实现无刷新,则需要在调用后端删除操作完成后,将调用refresh刷新操作,更换为调用FullCalendar的删除事件api,removeEvent,我们封装一个删除任务的方法如下:

// 删除任务
revmoveTask(taskId) {
  const fullCalendar = this.$refs.fullCalendar.calendar
  const event = fullCalendar.getEventById(taskId)
  event.remove()
}

修改任务

在日历视图中点击一个现有任务后,自动弹出修改任务对话框,如下图所示:
image.png
点击保存按钮后,使用emit触发父页面,也就是FullCalendar所在页面的refresh方法,跟上面新增原实现模式一致。

对于修改事件,FullCalendar并未提供一个像新增事件addEvent类似的事件,而是提供了一组事件。

需要通过日历对象的getEventById方法,通过事件id拿到事件对象。
然后调用事件对象的以下方法:
设置非时间相关的属性,使用event.setProp( name, value ),比如事件的名称
设置时间相关的属性,使用以下方法:
setStart
setEnd
setAllDay
设置扩展属性,使用event.setExtendedProp( name, value ),比如我们自定义的任务状态

对于修改任务,综合运用上述方法,重构如下:

// 修改任务
modifyTask(task) {
  const fullCalendar = this.$refs.fullCalendar.calendar
  const event = fullCalendar.getEventById(task.id)
  event.setProp('title', task.name)
  event.setStart(task.startTime)
  event.setEnd(task.endTime)
  let allDay = this.calculateAllDay(task.startTime, task.endTime)
  event.setAllDay(allDay)
  event.setExtendedProp('status', task.status)
  event.setExtendedProp('plannedDuration', task.plannedDuration)
}

这里测试发现存在小问题,当修改任务时,把起止时间都清空的情况下,应当把该任务视为待安排,从日历视图中移除,放回到收集箱中,而FullCalendar提供的setStart方法,不接受空值,设置空值无效,仍会显示原时间。

针对上述问题,调整如下:

 // 修改任务
  modifyTask(task) {
    const fullCalendar = this.$refs.fullCalendar.calendar
    const event = fullCalendar.getEventById(task.id)
    if (task.startTime) {
      // 开始时间有值,更新任务信息
      event.setProp('title', task.name)
      event.setStart(task.startTime)
      event.setEnd(task.endTime)
      let allDay = this.calculateAllDay(task.startTime, task.endTime)
      event.setAllDay(allDay)
      event.setExtendedProp('status', task.status)
      event.setExtendedProp('plannedDuration', task.plannedDuration)
    } else {
      // 开始时间无值
      // 从日历视图中移除任务
      event.remove(task.id)
      // 添加到收集箱中 TODO
    }
  }

如何添加到收集箱涉及到收集箱功能的无刷新改造,暂放,标记为todo,后面再说。

同时,在保存任务时,检测开始时间是否为空,如为空弹出确认框,说明该任务会自动放入收集箱,避免用户产生明明执行保存操作了,但日历中不显示的疑惑。

beforeSaveData() {
    if (!this.entityData.startTime) {
      // 开始时间为空,需用户确认是否继续
      return this.$confirm(
        '开始时间为空,该任务将放入收集箱,不会显示在日历中, 是否继续?',
        '确认',
        {
          type: 'warning'
        }
      )
    } else {
      // 开始时间不为空,直接返回
      return new Promise((resolve) => {
        resolve()
      })
    }
}

复制任务

之前实现的复制任务,是调用后端服务的复制新增功能,将新增后的数据返回回来,调用修改页面,传入新增记录的id来实现的,如下:

 if (command === 'copy') {
        this.$api.personaltask.task.addSingleByCopy(id).then((res) => {
          this.$refs.modifyPage.init(res.data.id)
        })
} 

在上面的修改任务的回调中,我们调用的是FullCalendar的修改事件的一系列api,这里是存在冲突的,即在复制这个场景下,我们应该最终调用的是FullCalendar的addEvent,来实现将通过复制新建的事件添加到日历中显示,因此调整如下:

引入modifyPage,将其组件命名修改为CopyPage,如下:

import CopyPage from '../task/modify.vue'

然后设定回调方法,如下:

<CopyPage ref="copyPage" @refresh="addTask" />

回调的依旧是新增任务的方法,跟前面新增页面保存的回调是一致的,如下:

 // 新增任务
addTask(task) {
  // 获取日历对象
  const fullCalendar = this.$refs.fullCalendar.calendar
  // 将任务数据转换为日历事件
  const event = this.convertTaskToEvent(task)
  // 调用api添加任务
  fullCalendar.addEvent(event)
}

退回收集

先前我们实现了通过右键菜单,将某个暂不具备的执行条件的任务退回了收集箱,采用的是整个页面刷新的模式。

// 设置待安排
setPending(id) {
  this.$api.personaltask.task.changeStatus(id, 'PENDING').then(() => {
    this.refresh()
  })
}

// 刷新
refresh() {
  const fullCalendar = this.$refs.fullCalendar.calendar
  // console.log(fullCalendar.view)
  let query = this.$route.query
  query = Object.assign(query, {
    viewType: fullCalendar.view.type,
    showAllFlag: this.showAllFlag,
    start: this.$dateFormatter.formatUTCDate(fullCalendar.view.activeStart),
    end: this.$dateFormatter.formatUTCDate(fullCalendar.view.activeEnd)
  })
  refreshSelectedTagWithQuery(query)
}

现改造为无刷新模式,当执行任务退回收集箱操作时,调用FullCalendar的删除任务的api,并调用收集箱的加载数据方法,来实现页面无刷新,如下:

// 设置待安排
setPending(id) {
  this.$api.personaltask.task.changeStatus(id, 'PENDING').then(() => {
    this.revmoveTask(id)
    this.reloadCollectionBox()
  })
}

// 删除任务
revmoveTask(taskId) {
  const fullCalendar = this.$refs.fullCalendar.calendar
  const event = fullCalendar.getEventById(taskId)
  event.remove()
}

// 刷新收集箱
reloadCollectionBox() {
  this.$refs.collectionBox.loadData()
}

应用系统

名称:遇见
地址:https://meet.popsoft.tech
说明:基于一二三应用开发平台和FullCalendar日历组件实现的面向个人的时间管理、任务管理系统,1分钟注册,完整功能,欢迎使用~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/802331.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java--数据结构】二叉树

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 树结构 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合 注意&#xff1a;树形结构中&#xff0c;子…

昇思MindSpore学习开始

昇思MindSpore是一个全场景深度学习框架&#xff0c;旨在实现易开发、高效执行、全场景统一部署三大目标。 其中&#xff0c;易开发表现为API友好、调试难度低&#xff1b;高效执行包括计算效率、数据预处理效率和分布式训练效率&#xff1b;全场景则指框架同时支持云、边缘以…

二叉树、B树/B-树

二叉树 在中文语境中,节点结点傻傻分不清楚,故后文以 node 代表 "结点",root node 代表根节点,child node 代表 “子节点” 二叉树是诸多树状结构的始祖,至于为什么不是三叉树,四叉树,或许是因为计算机只能数到二吧,哈哈,开个玩笑。二叉树很简单,每个 no…

在android11 上实现平行视界效果

前言&#xff1a; 平行视界是谷歌为了解决大屏横屏设备 适配为手机等竖屏设备开发的APP &#xff0c; 在这类APP显示时 在横屏设备上不方便用户观看。 android 13 上平行视界的效果如下&#xff1a; 正文: 在android13前 &#xff0c;各家有各自的解决方案&#xff0c;下面提…

[计算机网络] VPN技术

VPN技术 1. 概述 虚拟专用网络&#xff08;VPN&#xff09;技术利用互联网服务提供商&#xff08;ISP&#xff09;和网络服务提供商&#xff08;NSP&#xff09;的网络基础设备&#xff0c;在公用网络中建立专用的数据通信通道。VPN的主要优点包括节约成本和提供安全保障。 优…

心理健康服务小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;最新资讯管理&#xff0c;心理产品管理&#xff0c;产品分类管理&#xff0c;音乐理疗管理&#xff0c;试题管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;心理产品音…

学习大数据DAY17 PLSQL基础语法6和Git的基本操作

目录 包 存储过程调试功能 作业 阶段复习作业 Git课程目录 什么是版本控制 没有版本控制的缺点 常见的版本工具 版本控制分类 1. 本地版本控制 2. 集中版本控制 3. 分布式版本控制 Git与SVN主要区别 Git软件安装及配置 Windows系统安装Git 安装Tortoise Git(乌龟…

git和gitee的基本操作

目录 git常见命令 1.初始化工作区(在某一文件路径下) 2.查看当前工作区的代码文件状态 3.将工作区的代码文件提交到暂存区 4.将暂存区的代码文件提交到本地仓库 5.工作区和暂存区文件差异化比较 6.暂存区和本地仓库的差异化比较 7.工作区和本地仓库差异化比较 8.版本回…

自适应键盘,自带隐藏键盘的输入框(UITextField)

引言 在iOS开发中&#xff0c;输入框占据着举足轻重的地位。与安卓不同&#xff0c;iOS输入框经常面临键盘遮挡的问题&#xff0c;或者无法方便地取消键盘。为了解决这些问题&#xff0c;有许多针对iOS键盘管理的库&#xff0c;如IQKeyboardManager、TPKeyboardAvoiding和Keyb…

实习随笔【实现Json格式化与latex渲染】

【写在前面】在实习中&#xff0c;遇到了如下需求&#xff1a; 待格式化数据大概长这样&#xff0c;里面存在Json乱码以及由$$包裹的公式 目标格式&#xff1a; 一、Json格式化 我们这里的任务主要分为两部分&#xff1a; 解析一个可能包含嵌套的 JSON 字符串格式化 JSON 对象…

SAP ABAP性能优化分析工具

SAP系统提供了许多性能调优的工具&#xff0c;重点介绍下最常用几种SM50, ST05, SAT等工具&#xff1a; 1.工具概况 1.1 SM50 / SM66 - 工作进程监视器 通过这两个T-code, 可以查看当前SAP AS实例上面的工作进程&#xff0c;当某一工作进程长时间处于running的状态时&#…

支持前端路由权限和后端接口权限的企业管理系统模版

一、技术栈 前端&#xff1a;iview-admin vue 后端&#xff1a;springboot shiro 二、基于角色的权限控制 1、路由权限 即不同角色的路由访问控制 2、菜单权限 即不同角色的菜单列表展示 3、按钮权限 即不同角色的按钮展示 4、接口权限 即不同角色的接口访问控制 三…

C++——类和对象(下)

文章目录 一、再探构造函数——初始化列表二、 类型转换三、static成员静态成员变量静态成员函数 四、 友元友元函数友元类 五、内部类六、匿名对象 一、再探构造函数——初始化列表 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函…

16_网络IPC2-寻址

进程标识 字节序 采用大小模式对数据进行存放的主要区别在于在存放的字节顺序&#xff0c;大端方式将高位存放在低地址&#xff0c;小端方式将高位存放在高地址。 采用大端方式进行数据存放符合人类的正常思维&#xff0c;而采用小端方式进行数据存放利于计算机处理。到目前…

QT使用QPainter绘制多边形维度图

多边形统计维度图是一种用于展示多个维度的数据的图表。它通过将各个维度表示为图表中的多边形的边&#xff0c;根据数据的大小和比例来确定各个维度的长度。 一、简述 本示例实现六边形战力统计维度图&#xff0c;一种将六个维度的战力统计以六边形图形展示的方法。六个维度是…

WebAssembly与JavaScript的交互(1)

前一阵子利用Balazor开发了一个NuGet站点&#xff0c;对WebAssembly进行了初步的了解&#xff0c;觉得挺有意思。在接下来的一系列文章中&#xff0c;我们将通过实例演示的方式介绍WebAssembly的一些基本概念和编程模式。首先我们先来说说什么是WebAssembly&#xff0c;它主要帮…

微调 Florence-2 - 微软的尖端视觉语言模型

Florence-2 是微软于 2024 年 6 月发布的一个基础视觉语言模型。该模型极具吸引力&#xff0c;因为它尺寸很小 (0.2B 及 0.7B) 且在各种计算机视觉和视觉语言任务上表现出色。 Florence 开箱即用支持多种类型的任务&#xff0c;包括: 看图说话、目标检测、OCR 等等。虽然覆盖面…

LRC软件、Adobe Lightroom Classic软件多版本下载+LRC教程

简介&#xff1a; Adobe Lightroom Classic&#xff08;简称LR&#xff09;是Adobe Creative Cloud大家庭中的一款专业的图片管理和编辑工具&#xff0c;用于专业摄影师、摄影爱好者以及所有不断优化数码影像的人等。其目标是以丰富的功能提供高效、一致的体验&#xff0c;帮助…

php基础: 三角形

包含&#xff1a;左三角、左上三角、右三角、右上三角、等腰三角、倒等腰三角。注意空格的数量&#xff0c;因为*号后面加了空格 /*** * 左三角形* param $n* return void*/ function triangleLeft($n){echo <pre>;for ($i 1; $i < $n; $i) {for ($j 1; $j < $i…

对服务器进行基本了解(二)

目录 一. 云服务器数据库 1.查看MYSQL版本 2.查看mysql的运行状态 3.运行mysql 4. 进入mysql的用户 5. 更改用户密码 6. 查找mysql端口号 7. 创建一个数据库 8. 查看用户 9. 查看数据库 10. 显示数据库的表 11. 修改用户的host 12. 对用户赋权 13. 开放指定端…