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.calendarlet query = this.$route.queryquery = 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属性为trueif (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.contextMenuEventIdif (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.calendarconst 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.calendarconst 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.calendarconst 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.queryquery = 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.calendarconst event = fullCalendar.getEventById(taskId)event.remove()
}// 刷新收集箱
reloadCollectionBox() {this.$refs.collectionBox.loadData()
}

应用系统

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

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

C# modbus 图表

控件&#xff1a;chart1(图表)&#xff0c;cartesianChart1(第三方添加图表)&#xff0c;timer(时间) 添加第三方&#xff1a; 效果&#xff1a;图标会根据连接的温度&#xff0c;湿度用timer时间进行改变 Chart1控件样式&#xff1a;Series添加线条&#xff0c;颜色&#xf…

编程从零基础到进阶(更新中)

题目描述 依旧是输入三个整数&#xff0c;要求按照占8个字符的宽度&#xff0c;并且靠左对齐输出 输入格式 一行三个整数&#xff0c;空格分开 输出格式 输出它们按格式输出的效果&#xff0c;占一行 样例输入 123456789 -1 10 样例输出 123456789-1 10 #include "stdio.…

which 命令在Linux中是一个快速查找可执行文件位置的工具

文章目录 0、概念1、which --help2、which命令解释 0、概念 which命令用于查找命令的可执行文件的路径which 命令在 Linux 中用于查找可执行命令的完整路径。当你在 shell 中输入一个命令时&#xff0c;shell 会在环境变量 $PATH 定义的目录列表中查找这个命令。which 命令可以…

基于Python+Flask+SQLite的豆瓣电影可视化系统

FlaskMySQLEcharts 基于PythonFlaskSQLite的豆瓣电影可视化系统 Echarts 不支持登录注册&#xff0c;并且信息存储在数据库中 不含爬虫代码&#xff0c;或爬虫代码已失效 简介 基于PythonFlaskMySQL的豆瓣电影可视化系统&#xff0c;采用Echart构建图表&#xff0c;支持自定…

ARM架构与FreeRTOS中的内存管理(flash与SRAM,堆栈)

在ARM架构中&#xff0c;内存的地址空间是如何划分的&#xff0c;内存映射表是怎样的 在Cortex-M7中&#xff0c;存储器一共有4GB的地址空间&#xff0c;4GB的地址空间又被划分为8个区域块&#xff0c;每个块有512M的内存。 Note&#xff1a;4GB的地址空间为 0x0000 0000 - 0…

[C++] 深度剖析C_C++内存管理机制

文章目录 内存分布内存分布图解 C语言中动态内存管理方式malloc:callocrealloc C内存管理方式内置类型**自定义类型** operator new & operator deleteoperator new & operator delete函数operator newoperator delete **new T[N]** 与**delete[]** **定位new表达式(pl…

第二章 UI组件【Android基础学习】

第二章 UI组件【Android基础学习】 前言版权推荐开源第二章 UI组件2-1 布局管理器2-1-1 LinearLayout2-1-2 RelativeLayout 2-2 TextView2-3 Button2-4 EditText2-5 RadioButton2-6 复选框CheckBox2-7 ImageView2-8 列表视图 ListView2-9 网格视图 GridView2-10 滚动视图 Scrol…

深入理解Session和Cookie的作用与联系

深入理解Session和Cookie的作用与联系 1、什么是Cookie&#xff1f;1、什么是Session&#xff1f;1、Session和Cookie的联系4、实际应用场景 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Session和Cookie是两个至关重要的概念&#xff0c…

30.【C语言】详解printf

1.printf&#xff08;print formate&#xff09;输入函数 01.简单使用 调用前要引用头文件 #include <stdio.h> printf("abc"); 默认情况下打印完光标停留在同一行 \n可以换行 printf("abc\n"); ​ printf("ab\nc"); ​ printf(…

leetcode-383.赎金信

题源 383.赎金信 题目描述 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。magazine 中的每个字符只能在 ransomNote 中使用一次。示例 1&…

Beelzebub过程记录及工具集

文章目录 靶场搭建靶场测试过程安装dirsearch扫描目录wpscan扫描破解 靶场搭建 https://download.vulnhub.com/beelzebub/Beelzebub.zip 下载解压镜像&#xff0c;从vmware打开。 一键式开机即可。 打开后配置网络。 确保网络可达。 靶场测试过程 首先使用nmap扫描网段的存…

SpringBoot项目如何使用自定义Repository

在公司实习的时候&#xff0c;遇到这样一个问题&#xff0c;就是当往数据库添加记录的时候&#xff0c;需要先去查看数据库中的记录数是否超过了最大限制&#xff0c;如果没有超过则进行添加&#xff1b;否则就需要删除先前的记录从而保证数据库中的记录数。这样的话&#xff0…

【EXCELL技巧篇】使用Excel公式,获取当前 Excel的Sheet页的名字

【通知】&#xff1a; 正式跟大家说个难过的消息&#xff0c;本来在「中国朝代史」结束后&#xff0c;开启的下一个专栏「中国近代史」前面几期做的还好好的&#xff0c;可是今天起正式通知审核不过&#xff0c;因为一些原因。 其实我对于历史这一块我还是很感兴趣的&#xff0…

极地生产力自主采样系统的观测:融池比例统计 MEDEA 融池比例数据集

Observations from the Autonomous Polar Productivity Sampling System. 极地生产力自主采样系统的观测结果 简介 该项目是美国国家航空航天局 ICESCAPE 大型项目的一部分&#xff0c;旨在研究浮游植物丰度的长期季节性变化与整个生长季节在波弗特海和楚科奇海测量到的海冰…

从 Pandas 到 Polars 十八:数据科学 2025,对未来几年内数据科学领域发展的预测或展望

我在2021年底开始使用Polars和DuckDB。我立刻意识到这些库很快就会成为数据科学生态系统的核心。自那时起&#xff0c;这些库的受欢迎程度呈指数级增长。 在这篇文章中&#xff0c;我做出了一些关于未来几年数据科学领域的发展方向和原因的预测。 这篇文章旨在检验我的预测能力…

计算机技术与软件专业技术资格(软考)纸质证书邮寄方法

电子版证书已经有网友指出说明方法了&#xff0c;参考 软考电子证书下载 注意如果下载的PDF文件值无法打开的话&#xff0c;可以选择查看&#xff0c;然后 ctrlp 打印为PDF, 也是另外的一种下载方法&#xff1b; 下面说一下纸质版证书邮寄方法 1&#xff1a;登录网站 中国计…

【保姆级】Python项目部署到Linux生产环境(uwsgi+python+flask+nginx服务器)

1.安装python 我这里是3.9.5版本 安装依赖&#xff1a; yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make -y 根据自己的需要下载对应的python版本&#xff1a; cd /usr/local wget https://www.python.or…

CyberVadis认证是什么

CyberVadis认证是一项全球性的、权威的、基于云的网络安全性评估和认证项目。它是由Altimeter公司开发的&#xff0c;专门针对云计算服务提供商、数据中心、软件即服务(SaaS)供应商、安全咨询服务公司和内部IT部门而设计的。 CyberVadis认证旨在评估和验证组织在网络安全方面的…

数电基础 - 硬件描述语言

目录 一. 简介 二. Verilog简介和基本程序结构 三. 应用场景 四. Verilog的学习方法 五.调式方法 一. 简介 硬件描述语言&#xff08;Hardware Description Language&#xff0c;HDL&#xff09;是用于描述数字电路和系统的形式化语言。 常见的硬件描述语言包括 VHDL&…

邮箱表单系统源码

邮箱表单简介 我们的邮箱表单系统是一个简洁高效的工具&#xff0c;旨在为用户提供一种便捷的方式来提交他们的邮箱地址。该系统可以用于订阅新闻通讯、注册活动、获取用户反馈等多种场景。 功能特点&#xff1a; 用户友好的界面&#xff1a; 表单设计简洁直观&#xff0c;用…