Fullcalendar这个插件挺好,就是很多方法感觉官方文档也没怎么说,导致上手难度大,而且有些默认事件真的不太友好...废话不多说,先上效果图!

1、效果GIF

1.1 基本按钮功能

1.2  事件hover显示

1.3 事件添加、编辑、状态修改

1.4 日历事件搜索

2、 代码实现

2.1 Fullcalendar日历、el-popover弹窗

Fullcalendar@5.11.3引入后,要设置一大堆参数calendarOptions,包括显示时间区域、默认视图、是否显示全天类型、中文界面、事件的操作函数等,具体的一些设置内容,见下面代码的注释。

<template>         <FullCalendarclass="calendar"ref="fullCalendar":options="calendarOptions"><template v-slot:eventContent="arg"><el-popover:append-to-body="true"ref="popover1"placement="top-start"width="220":visible-arrow="true"trigger="hover":teleported="false"popper-class="popover":open-delay="100"@show="showPic(arg)"@hide="popoverPicReset(arg)"><el-row class="popover_title"><el-col:span="12":style="{color:arg.event.extendedProps.isDone == false? 'red': 'green',}"><spanstyle="padding-right: 2px":style="{'border-left':arg.event.extendedProps.isDone == false? '5px solid red': '5px solid green',}"></span>{{arg.event.extendedProps.isDone == false? "未开始": "已完成"}}</el-col><el-col:span="12"style="display: flex;flex-direction: row-reverse;font-size: 14;color: #000;">{{arg.event.allDay == true? "全天": formatTimer(arg.event.start)}}</el-col></el-row><el-row><el-col :span="24" style="text-align: center"><el-imagev-if="popoverimg.length != 0":src="popoverimg[0]"@click="PreviewPic(popoverimg)"fit="fill"class="popoverShowImg"><div slot="placeholder" class="image-slot">加载中<span class="dot">...</span></div></el-image><div class="block"></div></el-col></el-row><el-row class="popover_content"><el-col :span="24" style="max-height: 150px; overflow: auto"><span class="click">{{ arg.event.title }}</span></el-col><el-col :span="24"><el-linkv-if="arg.event.extendedProps.address != null &&arg.event.extendedProps.address != ''":href="undefined":underline="false"@click="fileDownload(arg.event.extendedProps.address)"class="link">{{arg.event.extendedProps.address == null? "": arg.event.extendedProps.address.replace("D:\\flask\\upload\\","")}}</el-link></el-col></el-row><el-row style="margin-top: 5px"><el-col style="width: 15%"><div><el-buttonclass="hvr-icon-pulse-grow":popperAppendToBody="false"size="mini"icon="el-icon-edit hvr-icon"type="primary"circle@click="handleEventClick(arg)"></el-button></div></el-col><el-col style="width: 15%"><el-buttonclass="hvr-icon-bounce"size="mini"type="success"icon="el-icon-document-checked hvr-icon"circle@click="onCheckBtnClicked(arg)"></el-button></el-col><el-col style="width: 15%"><el-popconfirmconfirm-button-text="好"cancel-button-text="否"icon="el-icon-info"icon-color="red"title="确定删除这个事项吗?"@confirm="onRemoveBtnClicked(arg)"><el-buttonclass="hvr-icon-buzz-out"slot="reference"size="mini"type="danger"icon="el-icon-delete hvr-icon"circle></el-button></el-popconfirm> </el-col></el-row><div slot="reference"><span class="tree_span_text">{{ arg.timeText }}</span><span>{{ arg.event.title }}</span></div></el-popover></template></FullCalendar>
</template>    <script>
import FullCalendar from "@fullcalendar/vue";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin from "@fullcalendar/interaction";
import listPlugin from "@fullcalendar/list";export default {// 注册局部组件components: {FullCalendar,Treeselect,"el-image-viewer": () =>import("element-ui/packages/image/src/image-viewer"),},data() {return {
// Fullcalendar版本@5.11.3日历控件设置项,官网文档地址:https://fullcalendar.io/calendarOptions: {visibleRange: { start: "2000-01-01", end: "2100-12-31" }, // 可视化区间,必须设置,否则查询列表不会显示事件// validRange: { start: "2021-09-01", end: "2021-09-01" }, // 可展示区间// 引入的插件,比如fullcalendar/daygrid,fullcalendar/timegrid引入后才可显示月,周,日plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin, listPlugin],initialView: "dayGridMonth", // 默认为那个视图(月:dayGridMonth,周:timeGridWeek,日:timeGridDay)firstDay: 1, // 设置一周中显示的第一天是哪天,周日是0,周一是1,类推locale: "zh-cn", // 切换语言,当前为中文allDaySlot: true, // 不显示all-daybusinessHours: true, //handleWindowResize: true, // 是否随浏览器窗口大小变化而自动变化。allDayText: "全天", // 设置all-Day显示的文字,不设置的话默认显示"all-Day"themeSystem: "bootstrap", // 主题色(本地测试未能生效)// loading: this.loadingEvent, // 视图数据加载中、加载完成触发(用于配合显示/隐藏加载指示器。)// selectAllow: this.selectAllow, //编程控制用户可以选择的地方,返回true则表示可选择,false表示不可选择// eventMouseEnter: this.eventMouseEnter, // 鼠标滑过allowContextMenu: false,editable: true, // 是否可以进行(拖动、缩放)修改eventStartEditable: true, // Event日程开始时间可以改变,默认true,如果是false其实就是指日程块不能随意拖动,只能上下拉伸改变他的endTimeeventDurationEditable: true, // Event日程的开始结束时间距离是否可以改变,默认true,如果是false则表示开始结束时间范围不能拉伸,只能拖拽selectable: true, // 是否可以选中日历格selectMirror: true,selectMinDistance: 0, // 选中日历格的最小距离// eventLimit: true, //数据条数太多时,限制各自里显示的数据条数(多余的以“+2more”格式显示),默认false不限制,支持输入数字设定固定的显示条数moreLinkContent: "+ 更多", //当一块区域内容太多以"+2 more"格式显示时,这个more的名称自定义// dayPopoverFormat: "YYYY-M-d",dayMaxEventRows: true, // 日历显示事件最大条数,for all non-TimeGrid viewsweekends: true, //navLinks: true, // 天链接selectHelper: false,slotEventOverlap: false, // 相同时间段的多个日程视觉上是否允许重叠,默认true允许aspectRatio: 1.35, //设置日历单元格宽度与高度的比例。expandRows: true,height: auto,contentHeight: 100,nowIndicator: true, //周/日视图中显示今天当前时间点(以红线标记),默认false不显示weekMode: "fixed", //在月视图里显示周的模式,因为每月周数可能不同,所以月视图高度不一定。fixed:固定显示6周高,日历高度保持不变liquid:不固定周数,高度随周数变化variable:不固定周数,但高度固定weekNumbers: true, //是否在日历中显示周次(一年中的第几周),如果设置为true,则会在月视图的左侧、周视图和日视图的左上角显示周数。weekText: "周",customButtons: {//自定义按钮getToday: {text: "今天",click: this.getToday,},getNext: {text: ">",click: this.getNext,},getPrev: {text: "<",click: this.getPrev,},getPrevYear: {text: "<<",click: this.getPrevYear,},getNextYear: {text: ">>",click: this.getNextYear,},customButton: {text: "今日标记全部已完成",click: this.customButton,},customButton1: {text: "显示未完成",click: this.customButton,},},// 日历头部按钮,即Fullcalendar表头显示区域// headerToolbar: {//   left: "getPrevYear,getPrev,getToday,getNext,getNextYear", //"getPrevYear,getPrev,getToday,getNext,getNextYear customButton,customButton1",//   center: "title",//   right://     "customButton customButton1 dayGridMonth,timeGridWeek,timeGridDay,listMonth", //dayGridWeek,listMonth// },headerToolbar: false, // Fullcalendar表头显示区域不显示,显示自己自定义的html头部// 使用内置按钮的显示文本buttonText: {today: "今天",month: "月",week: "周",day: "日",list: "日程",},// 设置日历显示事件时间头slotLabelFormat: {hour: "2-digit",minute: "2-digit",meridiem: false,hour12: false, // 设置时间为24小时},// 视图的一些基本设置views: {// 月视图阳历转农历dayGridMonth: {height: 500,displayEventTime: true, //是否显示时间dayMaxEventRows: 4, // adjust to 6 only for timeGridWeek/timeGridDay// titleFormat: { year: "numeric", month: "2-digit", day: "2-digit" }, //控制日历显示的标题// moreLinkContent: "+ 更多", //可放在这里单独对每个视图控制显示更多的文字moreLinkClick: "popover",eventTimeFormat: {hour: "numeric",minute: "2-digit",meridiem: false,},dayPopoverFormat: {month: "long",day: "numeric",year: "numeric",},// 显示农历// dayCellContent(item) {//   let _date = new Date(item.date).toLocaleDateString().split("/");//   let _dateF = calendar.solarToLunar(_date[0], _date[1], _date[2]);//   // 以二十四节气覆盖农历日期//   if (calendar.getLunar24Days(_date[0], _date[1], _date[2])) {//     _dateF.dayStr = calendar.getLunar24Days(//       _date[0],//       _date[1],//       _date[2]//     );//   }//   return { html: `<p>${item.dayNumberText}(${_dateF.dayStr})</p>` };// },},timeGridWeek: {},timeGridDay: {},listMonth: {},},// 设置过往时间无法点击// selectAllow: function (clickInfo) {//   if (clickInfo.end < new Date()) {//     return false;//   }//   return true;// },weekends: true, //是否显示周末,设为false则不显示周六和周日selectable: true, //是否可以选中日历格editable: false, //是否可以进行(拖动、缩放)修改navLinks: true, //天链接select: this.selectDate, //选中日历格事件eventClick: this.handleEventClick, //选中备忘录事件eventsSet: this.handleEvents,events: this.getCalendarList, //获取数据源eventMouseEnter: this.eventMouseEnter, //鼠标悬浮事件slotEventOverlap: true, //相同时间段的多个日程视觉上是否允许重叠,默认true允许eventResize: this.onEventResize, // 事件时间区间调整eventDrop: this.onEventResize, // 事件Drag-Drop事件eventMouseLeave: this.eventMouseLeave, // 鼠标移出事件发生的事件},};},
}</script>

在这里,鼠标在事件上面经过时,会显示一个弹出窗,如下图。可见,弹出框有:是否已完成,开始时间,图列说明(可以是图片、GIF等),文字说明、链接或是附件。以上的这些都是用el-popover实现,用了v-slot:eventContent="arg",将日历的数据进行处理。图片的显示需要修改源码才能显示,不然有bug显示不出来,修改的源码见此文章第3节内容

2.2 Fullcalendar日历自定义头部

在calendarOptions设置里,修改headerToolbar,设置为false。

然后写好自己的html代码,并调整好css样式。

绑定自定义按钮的函数功能,主要是利用了calendarApi自带的函数功能,包括视图切换、月日视图切换、往前和往后功能等,当然搜索功能是自己定义的。

  2.3 搜索功能

这里是onSearch函数功能,主要是在前端对events的过滤,然后再设置视图为list视图,注意这个视图在日历头部的功能区是没有的,但是是Fullcalendar内置的。当搜索框为空或者清空搜索字符后,需要重新请求后端数据。 

3、Fullcalendar源码修改

3.1 修改源码main.js的地址:

3.2 添加的show_pic函数/方法:

    CalendarApi.prototype.show_pic = function (arg) {var state = this.getCurrentData();this.unselect();// 出现图片的关键this.dispatch({type: 'CHANGE_DATE',dateMarker: state.dateEnv.createMarker(arg.view.currentStart),});};

4、Vue源码

<template><!-- el-mian是个人右侧容器的设置组件 --><el-main><div><!-- 日历头部div --><div class="fc-toolbar" style="display: flex; margin-bottom: 2px"><!-- 日历头部左侧显示区域 --><div class="fc-left" style="flex: 1; justify-content: flex-start"><div style="vertical-align: middle"><el-inputplaceholder="请输入查询内容"v-model="search_input"clearableclass="input"size="medium"@keyup.enter.native="onSearch"@clear="getToday()"><i slot="suffix" class="el-icon-search" @click="onSearch"></i></el-input></div></div><!-- 日历头部中间显示区域 --><divclass="fc-center"style="display: flex; flex: 3; justify-content: center"><el-button-group><el-buttonicon="el-icon-d-arrow-left"@click="getPrevYear"class="fc_btns"></el-button><el-buttonicon="el-icon-arrow-left"@click="getPrev"class="fc_btns"></el-button></el-button-group><h2 class="title">{{ title }}</h2><el-button-group><el-buttonicon="el-icon-arrow-right"@click="getNext"class="fc_btns"></el-button><el-buttonicon="el-icon-d-arrow-right"@click="getNextYear"class="fc_btns"></el-button></el-button-group></div><!-- 显示图标注释栏 --><!-- <div class="tips" style="display: flex"><divstyle="height: 14px;width: 14px;background: green;text-align: center;position: relative;top: 27%;"></div><span class="tip-content">已完成</span><divstyle="height: 14px;width: 14px;background: #fe9b02;text-align: center;position: relative;top: 27%;"></div><span class="tip-content">未开始</span></div> --><!-- 日历头部右侧显示区域 --><div class="fc-right"><el-button-group><el-button@click="today"type="success"plainsize="medium"class="fc_btns">今天</el-button><el-button@click="month"type="primary"plainsize="medium"class="fc_btns">月</el-button><el-button@click="week"type="primary"plainsize="medium"class="fc_btns">周</el-button><el-button@click="day"type="primary"plainsize="medium"class="fc_btns">日</el-button><el-button@click="list"type="primary"plainsize="medium"class="fc_btns">列表</el-button></el-button-group></div></div></div><!-- 日历本体 --><el-row><el-col :md="24" :xs="24"><div style="margin-top: 0px"><FullCalendarclass="calendar"ref="fullCalendar":options="calendarOptions"><template v-slot:eventContent="arg"><el-popover:append-to-body="true"ref="popover1"placement="top-start"width="220":visible-arrow="true"trigger="hover":teleported="false"popper-class="popover":open-delay="100"@show="showPic(arg)"@hide="popoverPicReset(arg)"><el-row class="popover_title"><el-col:span="12":style="{color:arg.event.extendedProps.isDone == false? 'red': 'green',}"><spanstyle="padding-right: 2px":style="{'border-left':arg.event.extendedProps.isDone == false? '5px solid red': '5px solid green',}"></span>{{arg.event.extendedProps.isDone == false? "未开始": "已完成"}}</el-col><el-col:span="12"style="display: flex;flex-direction: row-reverse;font-size: 14;color: #000;">{{arg.event.allDay == true? "全天": formatTimer(arg.event.start)}}</el-col></el-row><el-row><el-col :span="24" style="text-align: center"><el-imagev-if="popoverimg.length != 0":src="popoverimg[0]"@click="PreviewPic(popoverimg)"fit="fill"class="popoverShowImg"><div slot="placeholder" class="image-slot">加载中<span class="dot">...</span></div></el-image><div class="block"></div></el-col></el-row><el-row class="popover_content"><el-col :span="24" style="max-height: 150px; overflow: auto"><span class="click">{{ arg.event.title }}</span></el-col><el-col :span="24"><el-linkv-if="arg.event.extendedProps.address != null &&arg.event.extendedProps.address != ''":href="undefined":underline="false"@click="fileDownload(arg.event.extendedProps.address)"class="link">{{arg.event.extendedProps.address == null? "": arg.event.extendedProps.address.replace("D:\\flask\\upload\\","")}}</el-link></el-col></el-row><el-row style="margin-top: 5px"><el-col style="width: 15%"><div><el-buttonclass="hvr-icon-pulse-grow":popperAppendToBody="false"size="mini"icon="el-icon-edit hvr-icon"type="primary"circle@click="handleEventClick(arg)"></el-button></div></el-col><el-col style="width: 15%"><el-buttonclass="hvr-icon-bounce"size="mini"type="success"icon="el-icon-document-checked hvr-icon"circle@click="onCheckBtnClicked(arg)"></el-button></el-col><el-col style="width: 15%"><el-popconfirmconfirm-button-text="好"cancel-button-text="否"icon="el-icon-info"icon-color="red"title="确定删除这个事项吗?"@confirm="onRemoveBtnClicked(arg)"><el-buttonclass="hvr-icon-buzz-out"slot="reference"size="mini"type="danger"icon="el-icon-delete hvr-icon"circle></el-button></el-popconfirm> </el-col></el-row><div slot="reference"><span class="tree_span_text">{{ arg.timeText }}</span><span>{{ arg.event.title }}</span></div></el-popover></template></FullCalendar></div><!-- 事件添加或修改对话框 --><el-dialog:visible.sync="dialogVisible":popperAppendToBody="false"@close="cancel"v-dialogDrag:close-on-click-modal="false"class="calendar_matters"><div slot="title" class="header-title" :style="{ color: 'black' }"><i class="el-icon-edit"></i><span> &nbsp;事件</span></div><el-form ref="form" :model="form" label-width="80px"><el-row><el-col :span="12" :xs="24"><el-form-item label="事件时间"><div class="dateRange"><el-date-pickerv-model="dateRange"type="datetimerange"range-separator="至"start-placeholder="开始日期"end-placeholder="结束日期"></el-date-picker></div></el-form-item></el-col><el-col :span="12"> </el-col></el-row><el-row><el-col :span="24"><el-form-item label="具体事项"><el-inputv-model="form.remark"type="textarea"class="calendar_details"></el-input> </el-form-item></el-col></el-row><el-row><el-col :span="12" :xs="24"><el-form-item label="提醒类别" prop="deptId"><treeselectv-model="form.category":options="Options":props="defaultProps":show-count="true"placeholder="请选择类型"@select="categorySelected"/> </el-form-item></el-col><el-col :span="12" :xs="24"><el-form-itemv-if="form.userId == undefined"label="记录人"prop="userName"><el-inputv-model="form.userName"maxlength="30"disabledclass="userName"/> </el-form-item></el-col></el-row><el-row><el-col :span="12"> </el-col><el-col :span="12" :xs="24"><el-form-item label="状态"><el-radio-group v-model="form.isDone"><el-radio label="0">已完成</el-radio><el-radio label="1">未确认</el-radio></el-radio-group></el-form-item></el-col></el-row><el-row><el-col :span="18" :xs="24"><el-form-item label="附件" class="attachment" prop="address"><el-uploadaction="#":show-file-list="false":auto-upload="false":on-change="address_beforeupload"><div><el-buttontype="primary"icon="el-icon-folder-opened"></el-button></div></el-upload><el-inputv-model="form.address"clearable></el-input></el-form-item></el-col></el-row><el-row><el-col :span="24" :xs="24"><el-form-item label="相关图片" prop="address"><el-uploadref="uploadFile"class="upload-demo"action="#":auto-upload="false":show-file-list="true":on-change="beforeupload"list-type="picture-card":file-list="filelist"multiple><i slot="default" class="el-icon-plus"></i><div slot="file" slot-scope="{ file }"><imgclass="el-upload-list__item-thumbnail":src="file.url"alt=""/><span class="el-upload-list__item-actions"><spanclass="el-upload-list__item-preview"@click="handlePictureCardPreview(file)"><i class="el-icon-zoom-in"></i></span><spanv-if="!disabled"class="el-upload-list__item-delete"@click="handleDownload(file)"><i class="el-icon-download"></i></span><spanv-if="!disabled"class="el-upload-list__item-delete"@click="handleRemove(file)"><i class="el-icon-delete"></i></span></span></div></el-upload></el-form-item></el-col></el-row></el-form><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitForm">确 定</el-button><el-button @click="cancel">取 消</el-button></div></el-dialog><!-- 图片预览对话框 --><el-image-viewerv-if="img_dialogVisible":initial-index="0":on-close="onClose":url-list="dialogImageUrl"style="z-index: 3000"></el-image-viewer></el-col></el-row></el-main>
</template><script>
// import { getCalendarList } from "@/api/calendar.js";
import FullCalendar from "@fullcalendar/vue";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin from "@fullcalendar/interaction";
import listPlugin from "@fullcalendar/list";
// import tippy from "tippy.js";
// import "../../assets/tippy.css";
// import calendar from "../../utils/calendar.js";
// import { INITIAL_EVENTS, createEventId } from "./event-utils";
import { auto } from "@popperjs/core";
import "../../directives.js"; // v-dialogDrag: 弹窗可拖拽属性
import Treeselect from "@riophae/vue-treeselect"; // Treeselect插件
import "@riophae/vue-treeselect/dist/vue-treeselect.css"; // 若依css设置
export default {// 注册局部组件components: {FullCalendar,Treeselect,"el-image-viewer": () =>import("element-ui/packages/image/src/image-viewer"),},data() {return {// 搜索框输入的文本search_input: "",// treeselect插件默认配置defaultProps: {children: "children",label: "label",},// 提醒类别设置Options: [{id: "工作类别",pid: 0,label: "工作类别",children: [],},{id: "生活类别",pid: 0,label: "生活类别",children: [],},{id: "其他类别",pid: 0,label: "其他类别",children: [],},{id: "",pid: 0,label: "无",children: [],},],// 表单是否显示dialogVisible: false,// 表单标题栏title: "事件",// 表单当前编辑模式设置,add或amendform_edited_state: "",// 表单内容设置项form: {remark: undefined,isDone: undefined,img: "",address: "",},// 表单日期设置dateRange: [],// 图片预览的操作按钮是否展示disabled: false,// 图片是否显示img_dialogVisible: false,// 预览图片地址dialogImageUrl: "",// el-popover弹出框标题内容popover_title: "事项",// el-popover图片地址存放popoverimg: [],// 附件地址存放filelist: [],// ------------------// Fullcalendar版本@5.11.3日历控件设置项,官网文档地址:https://fullcalendar.io/calendarOptions: {// visibleRange: { start: "2021-09-01", end: "2022-10-01" }, // 可视化区间// validRange: { start: "2021-09-01", end: "2021-09-01" }, // 可展示区间// 引入的插件,比如fullcalendar/daygrid,fullcalendar/timegrid引入后才可显示月,周,日plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin, listPlugin],initialView: "dayGridMonth", // 默认为那个视图(月:dayGridMonth,周:timeGridWeek,日:timeGridDay)firstDay: 1, // 设置一周中显示的第一天是哪天,周日是0,周一是1,类推locale: "zh-cn", // 切换语言,当前为中文allDaySlot: true, // 不显示all-daybusinessHours: true, //handleWindowResize: true, // 是否随浏览器窗口大小变化而自动变化。allDayText: "全天", // 设置all-Day显示的文字,不设置的话默认显示"all-Day"themeSystem: "bootstrap", // 主题色(本地测试未能生效)// loading: this.loadingEvent, // 视图数据加载中、加载完成触发(用于配合显示/隐藏加载指示器。)// selectAllow: this.selectAllow, //编程控制用户可以选择的地方,返回true则表示可选择,false表示不可选择// eventMouseEnter: this.eventMouseEnter, // 鼠标滑过allowContextMenu: false,editable: true, // 是否可以进行(拖动、缩放)修改eventStartEditable: true, // Event日程开始时间可以改变,默认true,如果是false其实就是指日程块不能随意拖动,只能上下拉伸改变他的endTimeeventDurationEditable: true, // Event日程的开始结束时间距离是否可以改变,默认true,如果是false则表示开始结束时间范围不能拉伸,只能拖拽selectable: true, // 是否可以选中日历格selectMirror: true,selectMinDistance: 0, // 选中日历格的最小距离// eventLimit: true, //数据条数太多时,限制各自里显示的数据条数(多余的以“+2more”格式显示),默认false不限制,支持输入数字设定固定的显示条数moreLinkContent: "+ 更多", //当一块区域内容太多以"+2 more"格式显示时,这个more的名称自定义// dayPopoverFormat: "YYYY-M-d",dayMaxEventRows: true, // 日历显示事件最大条数,for all non-TimeGrid viewsweekends: true, //navLinks: true, // 天链接selectHelper: false,slotEventOverlap: false, // 相同时间段的多个日程视觉上是否允许重叠,默认true允许aspectRatio: 1.35, //设置日历单元格宽度与高度的比例。expandRows: true,height: auto,contentHeight: 100,nowIndicator: true, //周/日视图中显示今天当前时间点(以红线标记),默认false不显示weekMode: "fixed", //在月视图里显示周的模式,因为每月周数可能不同,所以月视图高度不一定。fixed:固定显示6周高,日历高度保持不变liquid:不固定周数,高度随周数变化variable:不固定周数,但高度固定weekNumbers: true, //是否在日历中显示周次(一年中的第几周),如果设置为true,则会在月视图的左侧、周视图和日视图的左上角显示周数。weekText: "周",customButtons: {//自定义按钮getToday: {text: "今天",click: this.getToday,},getNext: {text: ">",click: this.getNext,},getPrev: {text: "<",click: this.getPrev,},getPrevYear: {text: "<<",click: this.getPrevYear,},getNextYear: {text: ">>",click: this.getNextYear,},customButton: {text: "今日标记全部已完成",click: this.customButton,},customButton1: {text: "显示未完成",click: this.customButton,},},// 日历头部按钮,即Fullcalendar表头显示区域// headerToolbar: {//   left: "getPrevYear,getPrev,getToday,getNext,getNextYear", //"getPrevYear,getPrev,getToday,getNext,getNextYear customButton,customButton1",//   center: "title",//   right://     "customButton customButton1 dayGridMonth,timeGridWeek,timeGridDay,listMonth", //dayGridWeek,listMonth// },headerToolbar: false, // Fullcalendar表头显示区域不显示,显示自己自定义的html头部// 使用内置按钮的显示文本buttonText: {today: "今天",month: "月",week: "周",day: "日",list: "日程",},// 设置日历显示事件时间头slotLabelFormat: {hour: "2-digit",minute: "2-digit",meridiem: false,hour12: false, // 设置时间为24小时},// 视图的一些基本设置views: {// 月视图阳历转农历dayGridMonth: {height: 500,displayEventTime: true, //是否显示时间dayMaxEventRows: 4, // adjust to 6 only for timeGridWeek/timeGridDay// titleFormat: { year: "numeric", month: "2-digit", day: "2-digit" }, //控制日历显示的标题// moreLinkContent: "+ 更多", //可放在这里单独对每个视图控制显示更多的文字moreLinkClick: "popover",eventTimeFormat: {hour: "numeric",minute: "2-digit",meridiem: false,},dayPopoverFormat: {month: "long",day: "numeric",year: "numeric",},// 显示农历// dayCellContent(item) {//   let _date = new Date(item.date).toLocaleDateString().split("/");//   let _dateF = calendar.solarToLunar(_date[0], _date[1], _date[2]);//   // 以二十四节气覆盖农历日期//   if (calendar.getLunar24Days(_date[0], _date[1], _date[2])) {//     _dateF.dayStr = calendar.getLunar24Days(//       _date[0],//       _date[1],//       _date[2]//     );//   }//   return { html: `<p>${item.dayNumberText}(${_dateF.dayStr})</p>` };// },},timeGridWeek: {},timeGridDay: {},listMonth: {},},// 设置过往时间无法点击// selectAllow: function (clickInfo) {//   if (clickInfo.end < new Date()) {//     return false;//   }//   return true;// },weekends: true, //是否显示周末,设为false则不显示周六和周日selectable: true, //是否可以选中日历格editable: false, //是否可以进行(拖动、缩放)修改navLinks: true, //天链接select: this.selectDate, //选中日历格事件eventClick: this.handleEventClick, //选中备忘录事件eventsSet: this.handleEvents,events: this.getCalendarList, //获取数据源eventMouseEnter: this.eventMouseEnter, //鼠标悬浮事件slotEventOverlap: true, //相同时间段的多个日程视觉上是否允许重叠,默认true允许eventResize: this.onEventResize, // 事件时间区间调整eventDrop: this.onEventResize, // 事件Drag-Drop事件eventMouseLeave: this.eventMouseLeave, // 鼠标移出事件发生的事件},};},mounted() {this.calendarApi = this.$refs.fullCalendar.getApi();this.title = this.calendarApi.view.title;},watch: {search_input: {handler: function (newData, oldData) {if (newData == "") {this.month();this.getToday();}},deep: true,},},methods: {// 将当前时间移至今日事件today() {this.getToday();},// 月视图month() {this.calendarApi.changeView("dayGridMonth");this.title = this.calendarApi.view.title;},// 周视图week() {this.calendarApi.changeView("timeGridWeek");this.title = this.calendarApi.view.title;},// 日视图day() {this.calendarApi.changeView("timeGridDay");this.handleTime(this.calendarApi.currentData.dateProfile.activeRange);this.title = this.calendarApi.view.title;},// 列表视图list() {this.calendarApi.changeView("listMonth");this.title = this.calendarApi.view.title;},// 鼠标划过,使用tippy插件显示tooltipeventMouseEnter(info) {// 非周列表的情况下显示悬浮提示;// if (info.view.type != "listWeek") {//   tippy(info.el, {//     content: info.event.title,//     placement: "top-start",//   });// }},// 鼠标离开eventMouseLeave(arg) {// console.log("mouseleave");// arg.jsEvent.preventDefault();},// 事项调整时间区间事件onEventResize(arg) {let newTimeStart = this.dateFormat("YYYY-mm-dd HH:MM:SS",arg.event.startStr);let newTimeEnd = this.dateFormat("YYYY-mm-dd HH:MM:SS", arg.event.endStr);this.get("/calendar/updateTime", {id: arg.event.id,Start: newTimeStart,End: newTimeEnd,}).then((res) => {this.$message.success(res.data.info);});// 必须加这句,不然切换视图会有显示事件数目的buglet calendarApi = arg.view.calendar;calendarApi.today();},// el-popover图片点击预览放大事件PreviewPic(arg) {this.dialogImageUrl = arg;this.img_dialogVisible = true;this.stopMove();},// el-popover图片点击预览后关闭事件onClose() {this.img_dialogVisible = false;this.move();},// el-popover隐藏时触发,将图片地址修改为空async popoverPicReset(arg) {this.popoverimg = await [];let calendarApi = arg.view.calendar;calendarApi.show_pic(arg);},// el-popover显示图片功能async showPic(arg) {let calendarApi = arg.view.calendar;this.popoverimg = [];await this.post("/get_img_url", arg.event.extendedProps.img, "blob").then((res) => {// console.log(res.data.imgs);res.data.imgs.forEach((item, index) => {const img = "data:image/png;base64," + item;this.file = this.base64ImgtoFile(img); // 得到File对象const url =window.webkitURL.createObjectURL(this.file) ||window.URL.createObjectURL(this.file);this.popoverimg.push(url);});});calendarApi.show_pic(arg);},// 提醒类别选择categorySelected(node) {this.form.categoryName = node.label;},// 自定义按钮customButton() {console.log("点击了自定义按钮");},// 今天getToday() {let calendarApi = this.$refs.fullCalendar.getApi();calendarApi.today();this.handleTime(calendarApi.currentData.dateProfile.activeRange);this.title = this.calendarApi.view.title;this.search_input = "";},// 上一年getPrevYear() {let calendarApi = this.$refs.fullCalendar.getApi();calendarApi.prevYear();// this.handleTime(calendarApi.currentData.dateProfile.activeRange);this.title = this.calendarApi.view.title;},// 下一年getNextYear() {let calendarApi = this.$refs.fullCalendar.getApi();calendarApi.nextYear();// this.handleTime(calendarApi.currentData.dateProfile.activeRange);this.title = this.calendarApi.view.title;},// 上一月getPrev() {let calendarApi = this.$refs.fullCalendar.getApi();calendarApi.prev();// this.handleTime(calendarApi.currentData.dateProfile.activeRange);this.title = this.calendarApi.view.title;},// 下一月getNext() {let calendarApi = this.$refs.fullCalendar.getApi();calendarApi.next();// this.handleTime(calendarApi.currentData.dateProfile.activeRange);this.title = this.calendarApi.view.title;},// 处理时间格式handleTime(activeRange) {let result = {startStr: activeRange.start,endStr: activeRange.end,};this.getCalendarList();},// 获取列表信息getCalendarList(result) {// 以当前时间插入数据let _this = this;// 注意,请求的数据是数据库所有数据即可,不用考虑当前显示的时间范围,Fullcalendar会自动只显示当前日期范围的事件_this.get("/calendar/getCalendarList", "").then((res) => {_this.calendarOptions.events = [];res.data.data.forEach((item) => {var data = {id: item[0],title: item[1],start: item[2],end: item[3],allDay: item[4],className: item[5] == true ? "borderGreen" : "borderOrange",// 非标准字段:除上述字段外,您还可以在每个事件对象中包含自己的非标准字段。FullCalendar不会修改或删除这些字段。例如,开发人员通常包括描述在回调中使用的字段,如事件呈现挂钩. 任何非标准属性都将移动到extendedProps哈希期间事件解析.extendedProps: {isDone: item[5],img: item[6],address: item[7],type: item[8],},others: "该字段值会被自动归类到extendedProps里面",backgroundColor:(item[4] == true ? "all_Day" : "other") != "all_Day"? item[5] == true? "#c2fccd": "#FFECDC": "#66b1ff",editable: true, // 是否可以进行(拖动、缩放)修改};_this.calendarOptions.events.push(data);});}).catch((error) => {this.$message.error(error);});},// 选择日期,填写事件selectDate: function (arg) {let startTime = this.dateFormat("YYYY-mm-dd HH:MM", arg.start);let endTime = this.dateFormat("YYYY-mm-dd HH:MM", arg.end);this.dialogVisible = true;this.form_edited_state = "add";this.dateRange = [startTime, endTime]; // 设置当前记录事件的选择的时间段let info = JSON.parse(localStorage.getItem("userInfo")); // 获取当前记录人信息this.form.userName = info.username; // 设置记录人this.form.isDone = "1"; // 默认设置为事件未完成状态this.form.isAllDay = arg.allDay;let calendarApi = arg.view.calendar;calendarApi.unselect(); // 清除当前日期选择},// 表单确定按钮,提交事件submitForm() {let calendarApi = this.$refs.fullCalendar.getApi();var startTime = this.dateFormat("YYYY-mm-dd HH:MM:SS", this.dateRange[0]);var endTime = this.dateFormat("YYYY-mm-dd HH:MM:SS", this.dateRange[1]);if (this.form_edited_state == "add") {// 添加事件的后端数据请求this.get("/calendar/eventRecord",{isAllDay: this.form.isAllDay,dateRange: JSON.stringify([startTime, endTime]),remark: this.form.remark,type: this.form.type == undefined ? "" : this.form.type,isDone: this.form.isDone == "1" ? false : true,userName: this.form.userName,address: this.form.address == undefined ? "" : this.form.address,img: this.form.img == null ? "" : this.form.img,},"").then((res) => {this.$message.success("事件添加成功!");this.handleTime(calendarApi.currentData.dateProfile.activeRange);});} else if (this.form_edited_state == "amend") {// 修改事件的后端数据请求this.get("/calendar/submit",{id: this.form.id,dateRange: JSON.stringify([startTime, endTime]),remark: this.form.remark,type: this.form.type == undefined ? "" : this.form.type,isDone: this.form.isDone == "1" ? false : true,userName: this.form.userName,address: this.form.address == undefined ? "" : this.form.address,img: this.form.img == null ? "" : this.form.img,// type: this.form.category,},"").then((res) => {this.$message.success("修改事项成功!");if (this.search_input == "") {this.handleTime(calendarApi.currentData.dateProfile.activeRange);}});}this.dialogVisible = false;},// 表单取消按钮cancel() {this.form = {id: "",dateRange: "",remark: "",type: "",isDone: false,userName: "",address: "",img: "",};this.filelist = [];this.dialogVisible = false;this.form_edited_state = "";},// 点击事项事件handleEventClick(clickInfo) {this.dialogVisible = true;this.form_edited_state = "amend"; //修改状态var startTime = this.dateFormat("YYYY-mm-dd HH:MM:SS",clickInfo.event.startStr);var endTime = this.dateFormat("YYYY-mm-dd HH:MM:SS",clickInfo.event.endStr);// 设置打开对话框各部分的显示值this.form.id = clickInfo.event.id;this.dateRange = [clickInfo.event.start, clickInfo.event.end];this.form.remark = clickInfo.event.title;// 获取当前登录用户的名字let info = JSON.parse(localStorage.getItem("userInfo"));this.form.userName = info.username;this.form.isDone =clickInfo.event.extendedProps.isDone == true ? "0" : "1";this.form.img = clickInfo.event.extendedProps.img;this.form.address = clickInfo.event.extendedProps.address;this.form.category =clickInfo.event.extendedProps.type == undefined? "": clickInfo.event.extendedProps.type;// 请求后端图片URL方法this.post("/get_img_url", clickInfo.event.extendedProps.img).then((res) => {res.data.imgs.forEach((item, index) => {const img = "data:image/png;base64," + item;this.file = this.base64ImgtoFile(img); // 得到File对象const url =window.webkitURL.createObjectURL(this.file) ||window.URL.createObjectURL(this.file);this.filelist.push({name: res.data.origin_url[index],url: url,});});});},// 事项标记已完成或改为未完成的事件onCheckBtnClicked(arg) {this.get("/calendar/checked",{ id: arg.event.id, status: arg.event.extendedProps.isDone },"").then((res) => {this.calendarOptions.events.filter((item) => {if (item.id == arg.event.id) {item.extendedProps.isDone = !arg.event.extendedProps.isDone;arg.event.extendedProps.isDone == true? (item.className = "borderOrange"): (item.className = "borderGreen");item.backgroundColor =(arg.event.allDay == true ? "all_Day" : "other") != "all_Day"? arg.event.extendedProps.isDone == true? "#FFECDC": "#c2fccd": "#66b1ff";}return item;});this.$message.success("事件状态修改成功!");});},// 事项删除事件onRemoveBtnClicked(arg) {this.get("/calendar/remove", arg.event.id).then((res) => {this.calendarOptions.events = this.calendarOptions.events.filter((item) => {return item.id != arg.event.id;});});},// 当前事件绑定,此段代码可删掉handleEvents(events) {this.currentEvents = events;},// 提交上传文件submitFileForm() {/* 这里为啥会先发一个Option请求再发Post请求:这是浏览器处理跨域做的逻辑。CORS跨域请求会先发option请求,如果server返回access-control-allow-origin头为*或者和当前域名一致的话,才会进入第二段的真正请求。不然就会报 cross origin request is forbidden错误。*/this.$refs.upload.submit();},// 对话框图片预览放大handlePictureCardPreview(file) {this.dialogImageUrl = [file.url];this.img_dialogVisible = true;},// 预览图片下载到本地handleDownload(file) {// console.log(file, "@@@@");this.get("/bbx_img_download", file.name, "blob").then((res) => {var blob = new Blob([res.data], {type: "application/octet-stream;chartset=UTF-8",});var url = window.URL.createObjectURL(blob);var a = document.createElement("a");a.href = url;//文件名let name = res.config.params.replace("F:\\flask\\upload\\", "");a.download = name;a.click();window.URL.revokeObjectURL(url); // 释放掉blob对象});},// 下载文件fileDownload(file) {// console.log(this.urlToLink(file));// 判断是否为网页let bool = this.urlToLink(file);if (bool) {var b = document.createElement("a");b.setAttribute("href", file);b.setAttribute("target", "_blank");document.body.appendChild(b);b.click();} else {if (file != null || file != "") {this.get("/get_file_download", file, "blob").then((res) => {var blob = new Blob([res.data], {type: "application/octet-stream;chartset=UTF-8",});var url = window.URL.createObjectURL(blob);var a = document.createElement("a");a.href = url;//文件名let name = res.config.params.replace("F:\\flask\\upload\\", "");a.download = name;a.click();window.URL.revokeObjectURL(url); // 释放掉blob对象});} else {}}},// 移除对话框预览图片handleRemove(file) {let address = this.form.img.split(",");address = address.filter((item) => {return item != file.name;});address = address.join(",");this.form.img = address;const newArray = this.filelist.filter((item, index) => {return item.uid != file.uid;});this.filelist = newArray;},// 判断字符串是否为网页urlToLink(str) {var reg = /(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-)+)/g;if (reg.test(str)) {return true;} else {return false;}},// 停止页面滚动stopMove() {const m = (e) => {e.preventDefault();};document.body.style.overflow = "hidden";document.addEventListener("touchmove", m, false); // 禁止页面滑动},// 开启页面滚动move() {const m = (e) => {e.preventDefault();};document.body.style.overflow = "auto";document.removeEventListener("touchmove", m, true);},// 查询功能onSearch() {let search_text = this.search_input;let curr_Events = this.calendarOptions.events;if (search_text != "") {this.calendarApi.changeView("list");let result = this.searchStr(search_text, curr_Events);this.calendarOptions.events = result;this.title = "查询结果";} else {// this.today();}},// 数组中匹配单个字符串的方法,传入数组支持格式[{},{}],searchStr(str, arr) {let newList = [];// 要匹配字符串的首个字符let startChar = str.charAt(0);// 要匹配字符串的字符长度let strLength = str.length;for (let i = 0; i < arr.length; i++) {// 默认数组arr中对象arr[i]不存在strlet isExist = false;let obj = arr[i];for (let key in obj) {if (typeof obj[key] === "function") {obj[key]();} else {let keyValue = "";// 获取arr[i][key]的值if (obj[key] !== null && typeof obj[key] === "string") {keyValue = obj[key];} else if (obj[key] !== null && typeof obj[key] !== "string") {keyValue = JSON.stringify(obj[key]);}// arr[i][key]中的各个位置的字符与str的0位置字符startChar对比如果相等,// 在arr[i][key]中从j位置截取与str长度相同的字符,对比是否相等for (let j = 0; j < keyValue.length; j++) {// 把原有数据转化为小写,输入数据也转化为纯小写,实现模糊匹配,如区分大小写,可删除toLowerCase()if (keyValue.charAt(j).toLowerCase() === startChar.toLowerCase()) {if (keyValue.substring(j).substring(0, strLength).toLowerCase() === str.toLowerCase()) {// 模糊匹配到的字符存在表示arr[i]中存在strisExist = true;break;}}}}}// 当arr[i]中存在str时,把arr[i]放入一个新数组if (isExist === true) {newList.push(obj);}}// 最后返回这个新数组return newList;},// 格式化时间 fmt是所需格式化的格式,如"YYYY-mm-dd HH:MM:SS",date是所需格式化的日期dateFormat(fmt, date) {let ret = "";date = new Date(date);const opt = {"Y+": date.getFullYear().toString(), // 年"m+": (date.getMonth() + 1).toString(), // 月"d+": date.getDate().toString(), // 日"H+": date.getHours().toString(), // 时"M+": date.getMinutes().toString(), // 分"S+": date.getSeconds().toString(), // 秒};for (let k in opt) {ret = new RegExp("(" + k + ")").exec(fmt);if (ret) {fmt = fmt.replace(ret[1],ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"));}}return fmt;},//日期格式转换formatTimer: function (value) {let date = new Date(value);let y = date.getFullYear();let MM = date.getMonth() + 1;MM = MM < 10 ? "0" + MM : MM;let d = date.getDate();d = d < 10 ? "0" + d : d;let h = date.getHours();h = h < 10 ? "0" + h : h;let m = date.getMinutes();m = m < 10 ? "0" + m : m;let s = date.getSeconds();s = s < 10 ? "0" + s : s;return h + ":" + m;},// 获取当前时间戳getCurrentTime() {let date = new Date();let year = date.getFullYear();let month = date.getMonth() + 1;let day = date.getDate();let hour = date.getHours();let minute = date.getMinutes();let second = date.getSeconds();month = month < 10 ? "0" + month : month; // 可注释掉day = day < 10 ? "0" + day : day;hour = hour < 10 ? "0" + hour : hour; // 可注释掉minute = minute < 10 ? "0" + minute : minute;second = second < 10 ? "0" + second : second;return `${year}/${month}/${day} ${hour}:${minute}:${second}`;},// 地址上传地址address_beforeupload(file, filelist) {let formData = new FormData();formData.append("file", file.raw);this.upload("/get_file_url", formData, "").then((res) => {this.form.address = res.data.path;});},/** 图片上传功能 */beforeupload(file, filelist) {this.filelist = filelist;let formData = new FormData();formData.append("file", file.raw);// 请求数据this.upload("/get_file_url", formData, "").then((res) => {if (this.form.img == null) {this.form.img = res.data.path;} else {this.form.img = this.form.img + "," + res.data.path;}});},// base64字段变成blob二进制数据,关于图片的base64ImgtoFile(dataurl, filename = "file") {const arr = dataurl.split(",");const mime = arr[0].match(/:(.*?);/)[1];const suffix = mime.split("/")[1];const bstr = atob(arr[1]);let n = bstr.length;const u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], `${filename}.${suffix}`, {type: mime,});},},
};
</script><!-- 样式1,本地样式 -->
<style lang="scss" scoped>
.calendar_matters >>> .el-dialog__body {height: 450px;overflow: auto;
}
.calendar_details >>> .el-textarea__inner {font-weight: bold;font-family: Arial, Helvetica, sans-serif;color: #000;height: 120px;
}
.calendar_matters >>> .el-dialog__header {border-radius: 5px;background-color: #cae1f7;align-content: center;padding: 15px;font-weight: bold;border-bottom-style: solid;border-bottom-width: 1px;border-bottom-color: aliceblue;
}
.fc-daygrid-day-top p {font-size: 13px;color: #606266;margin-right: 10px;
}
.fc .fc-toolbar.fc-header-toolbar {margin-bottom: 10px;
}
.el-main {padding: 8px 10px 8px 10px;
}
.userName >>> .el-input__inner {font-weight: bold;color: #000000ab;
}
.attachment >>> .el-form-item__content {display: flex;
}
.upload-demo >>> .el-upload-list--picture-card .el-upload-list__item {height: 120px;width: 120px;
}
.upload-demo >>> .el-upload--picture-card {height: 120px;width: 120px;line-height: 120px;
}
.dateRange >>> .el-range-input {font-weight: bold;color: #080808;
}
.calendar >>> .fc-header-toolbar {margin-bottom: 5px;
}
.calendar >>> .borderGreen {border-left: 5px solid #44bb08 !important;border-radius: 0;border: none;white-space: normal;overflow: hidden;text-overflow: ellipsis;span {color: #000;font-weight: bold;}
}
.calendar >>> .borderOrange {border-left: 5px solid #fe9b02 !important;border-radius: 0;border: none;white-space: normal;overflow: hidden;span {color: #000;font-weight: bold;}
}
.calendar >>> .borderOrigin {border-radius: 0;border: none;white-space: normal;overflow: hidden;text-overflow: ellipsis;max-height: 150px;span {color: #000;font-weight: bold;}
}
.calendar >>> .fc-event-title {font-weight: bold;color: #000;overflow: hidden;
}
.calendar >>> .fc-event-time {font-weight: bold;color: #000;
}
.calendar >>> .fc-daygrid-event-dot {border: none;
}
.popover {.el-popover {max-height: 350px;}
}
.popover_title {font-weight: bold;margin-bottom: 3px;
}
.popover_content {color: #000;font-size: 13px;
}
.popoverShowImg {width: auto;cursor: pointer;>>> .el-image__inner {max-height: 200px;}
}
.link {color: #000;font-size: 12px;font-weight: bold;margin-top: 2px;& :hove {color: #66b1ff;}
}
.fc_btns {padding: 10px 12px;
}
.el-icon-search {line-height: 2.5;margin-right: 8px;
}
.fc-right {display: flex;flex: 1.5;justify-content: flex-end;
}
.tip-content {line-height: 2.2;margin-right: 4px;font-weight: 600;
}
.title {margin: 0px 5px;line-height: 1.6;
}
.calendar_matters {width: 100%;
}
.calendar >>> .el-popover__reference {display: grid;max-height: 150px;overflow: auto;
}
</style><!-- 样式2 --><style>
.el-popover__reference::-webkit-scrollbar {/*滚动条整体样式*/width: 8px; /*高宽分别对应横竖滚动条的尺寸*/height: 1px;
}
.el-popover__reference::-webkit-scrollbar-thumb {/*滚动条里面小方块*/border-radius: 10px;-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);background: #535353;
}
.el-popover__reference::-webkit-scrollbar-track {/*滚动条里面轨道*/-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);border-radius: 2px;background: #ededed;
}
.popover .el-popover__title {font-weight: bold;margin-bottom: 5px;border-bottom: solid 1px;padding: 2px;
}
@media only screen and (max-width: 767px) {.fc-toolbar {flex-direction: column;}.fc-left {flex: 1 !important;}.tips {flex: 1 !important;justify-content: center;margin-bottom: 5px;}.fc-center {flex: 1 !important;margin: 5px 0px;}.fc-right {flex: 1;justify-content: center !important;margin-bottom: 5px;}.tip-content {line-height: 1 !important;margin-right: 4px;font-weight: 600;}.title {font-size: 20px;font-weight: 700;line-height: 1.9 !important;}.fc-list-table {word-break: break-all;overflow: auto;}.fc-list-event-title {overflow: auto;}.el-dialog {width: 90%;}.dateRange {overflow: auto;}
}
</style>

5、 后端Flask源码

from flask import Flask, render_template,\request, jsonify, make_response, Response, send_file,session,send_from_directory
from flask_cors import CORS
import datetime
import json
import pypyodbc
from collections import deque
import os
import base64
import hashlib
import xlwt
import openpyxl
import xlrdapp = Flask(__name__,static_folder='./templates/static',  # 设置静态文件夹目录template_folder="./templates")  # 设置vue编译输出目录dist文件夹,为Flask模板文件目录
# 解决后端跨域问题,不然会在前端网页控制台显示“ccess to XMLHttpRequest at 'http://localhost:8080/api/login' from origin 'null' has been blocked”
CORS(app, supports_credentials=True)
session = {}@app.route('/')
def index():return render_template('index.html',name='index') #使用模板插件,引入index.html。此处会自动Flask模板文件目录寻找index.html文件。@app.route('/calendar/eventRecord', methods=["GET", "POST"])
def calendar_evnetRecord():params = request.valuesdate = json.loads(params["dateRange"])startTime = datetime.datetime.strptime(date[0],"%Y-%m-%d %H:%M:%S")endTime = datetime.datetime.strptime(date[1],"%Y-%m-%d %H:%M:%S")# print(params)conn = get_conn()cur = conn.cursor()sql = "insert into 记事本(allDay, startStr,endStr, title, type, isDone, userName,img,address) values(%s, '%s','%s','%s','%s',%s,'%s','%s','%s')" \%(params["isAllDay"],startTime,endTime,params["remark"],params["type"],params["isDone"],params["userName"],params["img"],params["address"])cur.execute(sql)cur.commit()cur.close()conn.close()return jsonify({"code":200})@app.route('/calendar/getCalendarList', methods=["GET", "POST"])
def calendar_getCalendarList():params = request.valuesconn = get_conn()cur = conn.cursor()sql = "select ID,title,Format(startStr, 'yyyy-MM-dd HH:mm:ss'),Format(endStr, 'yyyy-MM-dd HH:mm:ss'),allDay,isDone,img,address,type " \"from 记事本 "cur.execute(sql)data = cur.fetchall()cur.close()conn.close()return jsonify({"code":200,"data":data})@app.route('/calendar/submit', methods=["GET", "POST"])
def calendar_submit():params = request.valuesdate = json.loads(params["dateRange"])startTime = datetime.datetime.strptime(date[0],"%Y-%m-%d %H:%M:%S")endTime = datetime.datetime.strptime(date[1],"%Y-%m-%d %H:%M:%S")conn = get_conn()cur = conn.cursor()sql="update 记事本 set startStr='%s',endStr='%s',title='%s',type='%s',isDone=%s,userName='%s',img='%s',address='%s' where ID=%s"\%(startTime,endTime,params["remark"],params["type"],params["isDone"],params["userName"],params["img"],params["address"],params["id"])cur.execute(sql)cur.commit()cur.close()conn.close()return jsonify({"code":200})@app.route('/calendar/checked', methods=["GET", "POST"])
def calendar_checked():params = request.valuesstatus = True if params["status"] == "false" else Falseconn = get_conn()cur = conn.cursor()sql = "update 记事本 set isDone=%s where ID=%s" %(status,params["id"])cur.execute(sql)cur.commit()cur.close()conn.close()return jsonify({"code": 200, "info":"状态修改成功!"})@app.route('/calendar/remove', methods=["GET", "POST"])
def calendar_remove():matters_id = request.values.get("0")conn = get_conn()cur = conn.cursor()sql = "delete from 记事本 where ID=%s" % (matters_id)try:cur.execute(sql)cur.commit()return jsonify({"code": 200, "info":"删除成功!"})except Exception as e:return jsonify({"code": 200, "info":"发生错误!错误代码:"+str(e)})finally:cur.close()conn.close()@app.route('/calendar/updateTime', methods=["GET", "POST"])
def calendar_updateTime():params = request.valuesconn = get_conn()cur = conn.cursor()sql = "update 记事本 set startStr='%s',endStr='%s' where ID=%s" %(params["Start"],params["End"],params["id"])cur.execute(sql)cur.commit()cur.close()conn.close()return jsonify({"code": 200, "info": "事件时间修改成功!"})@app.route('/get_file_download', methods=["GET", "POST"])
def get_file_download():file_url = request.values.get("0")with open(file_url, 'rb') as file_f:res = make_response(file_f.read())  # 用flask提供的make_response 方法来自定义自己的response对象# res.headers['Content-Type'] = 'image/jpg'  # 设置response对象的请求头属性'Content-Type'为图片格式return resif __name__ == '__main__':# 0.0.0.0 表示同一个局域网均可访问,也可以替换成本机地址:通过命令行命令:ipcofig 获取app.run(host='0.0.0.0', port='5000', debug=True)

6 、数据库数据(ACCESS)格式

Fullcalendar日历使用,包括视图选择、事件插入、编辑事件、事件状态更改、事件添加和删除、事件拖动调整,自定义头部,加入el-popover显示图片、图片预览、添加附件链接等,支持手机显示。相关推荐

  1. 仿淘宝中的评价晒单中选择图片可以预览,添加,删除

    前一篇文章写了如何把input="file"的默认样式改为自己想要的样式所以这篇是在这个的基础上写的,也就是说改变成了自己想要的样式之后,点击选择图片,可以放在一个div中预览,同 ...

  2. 微信小程序选择图片和预览图片

    作者>:燕潇洒 视频中,老师也是看着官方文档,为学生们讲解,微信提供了系统的方法来选择图片. wx.chooseImage({}) 此方法是用来选择图片的方法,具体使用如下: data: {av ...

  3. 微信小程序 - 选择图片、预览图片、删除图片

    为什么80%的码农都做不了架构师?>>>    小程序开发中你可能会遇到上传的图片首先会选择图片之后预览看下或者不满意需要删除图片,才会提交吧,这样应该是一个比较好的处理方式. 微信 ...

  4. 不同手机型号图文预览_微信编辑器预览一般以多大的手机尺寸为准?

    在此前,使用微信编辑器编辑好文章后,大家需要将图文在手机上进行预览,确认没有问题后才会在公众号或者自媒体平台上进行发布,这样来回切换平台浏览自然比较麻烦,有需求就有供给,市面上不少编辑器就有提供模拟手 ...

  5. javascript显示本地服务器图片,JavaScript图片本地预览功能的实现方法

    这篇文章主要介绍了JavaScript实现图片本地预览功能,针对非IE浏览器的HTML5滤镜功能及IE浏览器的相关组件功能实现不上传至服务器预览本地图片的效果,需要的朋友可以参考下 本文实例讲述了Ja ...

  6. SpringBoot 实现万能文件在线预览,已开源,支持offcie、office365等,真香!!

    大家好,我是东哥. 推荐一个用 Spring Boot 搭建的文档在线预览解决方案:kkFileView,一款成熟且开源的文件文档在线预览项目解决方案,对标业内付费产品有永中office.office ...

  7. 微信开发php插件下载图片,微信开发之微信jsapi选择图片,上传图片,预览和下载图片方法...

    参数描述 appId公众号的唯一标识 应用id timestamp生成签名的时间戳 nonceStr生成签名的随机串 signature签名 上述表格中的四个参数是初始化调用微信jsapi的凭证,咱们 ...

  8. H5开发:使用H5、CSS、JS、JQUERY实现从本地选择图片、预览图片、上传图片列表

    需求描述 使用H5.CSS.JS.JQUERY 点击添加图片按钮,从本地选择图片(同名图片不可重复选择),在页面预览选中的图片 点击图片查看大图,点击图片右上角"×"删除此图 点击 ...

  9. input选择视频或图片本地预览问题

    图片目前可以用两个方法,示例是vue代码,原生用的话需要小改下 预览图片 1 <input id="filepicker" @change="chooseImgIn ...

最新文章

  1. C++模板之核心:typename
  2. BRCM SDK 版本IPv6问题
  3. 生成随机数 java
  4. C#中ToString()格式详解
  5. 王欣的“灵鸽”到底行不行?
  6. 九宫怎么排列和使用_开心数独:好友都在玩的数独九宫格小游戏,居家无聊杀时间必备!...
  7. 【转载】浅谈嵌入式MCU开发中的三个常见误区
  8. 想不明白:为什么龙芯取消了MIPS版OpenJDK8开源
  9. paip.项目开发效率提升之思索
  10. iOS-常用宏定义大全
  11. php爬虫框架使用案例QueryList,将数据爬到mysql数据库
  12. mac 恢复未能与服务_MacBook Pro无法与恢复服务器取得联系?
  13. 400 行 C 代码实现一个虚拟机
  14. 草根互联网经理掀起的中国性解放运动
  15. 1-3 Burpsuite 抓取手机APP流量
  16. FastStone Capture安装包正版激活码使用说明
  17. Codeforces Round #533 (Div. 2) D. Kilani and the Game
  18. C语言中,定义一个布尔型变量如何用,为什么我用bool定义会报错?
  19. BL9342降压型电源芯片
  20. 学员故事:老男孩Linux运维班学习五个月,让我实现月薪万元+

热门文章

  1. 计算两个一维数组的卷积
  2. kafka logManager类 kafka存储机制
  3. 不限专业的计算机证书,不限专业和工作年限就能报考的证书有哪些?
  4. vue中使用vue-video-player实现直播推流播放m3u8
  5. 常见的设计模式和使用场景
  6. 2020Java书单推荐
  7. AIGC创投现状:激情与焦虑同行 | 圆桌论坛@中国AIGC产业峰会
  8. 计算机图形学 实验8 《复杂图形绘制-Bezier曲面及其纹理》
  9. [文献阅读]BeatGAN: Anomalous Rhythm Detection using Adversarially Generated Time Series
  10. Atlas800-9000 开发环境搭建