当前位置: 首页 > article >正文

Vue 项目中如何使用FullCalendar 时间段选择插件(类似会议室预定、课程表)

在这里插入图片描述
本文中是基于VUE+elementui项目中实现的前后端分离的前端功能部分:

插件的官方文档:FullCalendar

1.安装对应依赖(统一安装版本为6.15)

npm install  --save @fullcalendar/core@6.15
npm install  --save @fullcalendar/daygrid@6.15
npm install  --save @fullcalendar/interaction@6.15
npm install  --save @fullcalendar/moment@6.15
npm install  --save @fullcalendar/resource@6.15
npm install  --save @fullcalendar/resource-timeline@6.15
npm install  --save @fullcalendar/timegrid@6.15
npm install  --save @fullcalendar/vue@6.15

2.放置组件展示的div(包含选中之后的弹框)

<template>
  <div class="app-container meetingroomApply">
    <el-button
      type="primary"
      icon="el-icon-plus"
      size=" medium"
      @click="openDialog"
      style="margin-bottom: 10px;"
      >会议室预定</el-button
    >
    <div class="fullCalendar" id="calendar"></div>
    <div class="tips-text">请用鼠标滑动选择时间段进行会议预约!</div>

    <el-dialog
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
      :title="title"
    >
      <el-form
        ref="form"
        :model="form"
        :rules="rules"
        label-width="110px"
        style="padding: 10px 10px"
      >
        <el-form-item label="会议室" prop="meetingroomName">
          <el-select
            v-model="form.meetingroomName"
            placeholder="请选择会议室"
            @change="roomChange"
            :disabled="this.eventClickFlag ? true : false"
          >
            <el-option
              v-for="room in meetingroomList"
              :key="room.id"
              :label="room.meetingroomName"
              :value="room.meetingroomName"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="会议名称" prop="meetingName">
          <el-input
            placeholder="请输入会议名称"
            v-model="form.meetingName"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
        <el-form-item label="会议开始时间" prop="meetingStartTime">
          <el-date-picker
            clearable
            v-model="form.meetingStartTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择会议开始时间"
            :disabled="this.eventClickFlag ? true : false"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="会议结束时间" prop="meetingEndTime">
          <el-date-picker
            clearable
            v-model="form.meetingEndTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择会议结束时间"
            :disabled="this.eventClickFlag ? true : false"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="预定人" prop="userName">
          <el-input
            v-model="form.userName"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
        <el-form-item label="预定人电话" prop="userPhone">
          <el-input
            v-model="form.userPhone"
            placeholder="请输入联系电话"
            :disabled="this.eventClickFlag ? true : false"
          />
        </el-form-item>

        <el-form-item label="预定人公司" prop="companyId">
          <el-input
            v-model="form.companyId"
            type="text"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
        <el-form-item label="预定部门" prop="deptId">
          <el-input
            v-model="form.deptId"
            type="text"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
        <el-form-item label="参会人" prop="participant">
          <el-input
            v-model="form.participant"
            type="textarea"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
        <el-row>
          <el-col :span="6">
            <el-form-item label="投屏" prop="projectionScreen">
              <el-checkbox
                v-model="form.projectionScreen"
                :true-label="1"
                :false-label="0"
                :disabled="this.eventClickFlag ? true : false"
              ></el-checkbox>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="桌牌" prop="tableBrand">
              <el-checkbox
                v-model="form.tableBrand"
                :true-label="1"
                :false-label="0"
                :disabled="this.eventClickFlag ? true : false"
              ></el-checkbox>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="瓶装水" prop="bottleWater">
              <el-checkbox
                v-model="form.bottleWater"
                :true-label="1"
                :false-label="0"
                :disabled="this.eventClickFlag ? true : false"
              ></el-checkbox>
            </el-form-item>
          </el-col>
        </el-row>

        <el-form-item label="其他需要" prop="otherNeeds">
          <el-input
            v-model="form.otherNeeds"
            type="textarea"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input
            v-model="form.remark"
            type="textarea"
            :disabled="this.eventClickFlag ? true : false"
          ></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button
          type="primary"
          :loading="buttonLoading"
          @click="submitForm"
          v-if="!this.eventClickFlag"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

3.对应的js代码

<script>
import FullCalendar from "@fullcalendar/vue";
import { Calendar } from "@fullcalendar/core";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import resourceTimelinePlugin from "@fullcalendar/resource-timeline";
export default {
  components: {
    FullCalendar, // 像使用自定义组件一样使用
  },
  constructor() {
    const name = Calendar.name;
  },
  data() {
    return {
      // 显示搜索条件
      showSearch: true,
      // 按钮loading
      buttonLoading: false,
      dialogVisible: false,
      eventClickFlag: false,
      title: "",
      // 部门树选项
      deptOptions: [],
      deptOptionsLink: [], //根据公司id查询部门数据
      // 公司名称数组
      companyList: [],
      meetingroomList: [],
      meetingroomEventList: [],
      form: {
        meetingName: "",
        participant: "",
        projectionScreen: "",
        tableBrand: "",
        bottleWater: "",
        otherNeeds: "",
        remark: "",
        meetingStartTime: "",
        meetingEndTime: "",
        meetingroomName: "",
        meetingroomId: "",
      },
      rules: {},
      // 查询参数
      queryParams: {
        meetingroomNo: undefined,
        meetingroomName: undefined,
        meetingDay: undefined,
        viewType: undefined,
        beginDayTime: undefined,
        endDayTime: undefined,
        year: undefined,
        month: undefined,
      },
      meetingroomList: [],
      meetingroomEventList: [],
      //监听到的当前view模式
      viewType: "",
      calendar: null,
      calendarOptions: {
        //   timeGridPlugin  可显示每日时间段
        height: 700,
        allDaySlot: false, //是否在日历上方显示all-day(全天)
        axisFormat: "h(:mm)tt",
        plugins: [
          dayGridPlugin,
          interactionPlugin,
          timeGridPlugin,
          resourceTimelinePlugin,
        ],
        headerToolbar: {
          left: "prev,next today",
          center: "title",
          // right: "dayGrid,dayGridWeek,dayGridMonth",
          right: "resourceTimelineDay,resourceTimelineWeek,dayGridMonth",
        },
        buttonText: {
          // 设置按钮
          today: "今天",
          day: "日",
          week: "周",
          month: "月",
        },
        editable: true,
        selectable: true,
        navLinks: true,
        datesSet: this.datesSet, //日期渲染;修改日期范围后触发
        // displayEventEnd: true,//所有视图显示结束时间
        // initialView: "dayGrid", // 设置默认显示周,可选周、日
        initialView: "resourceTimelineDay",
        dateClick: this.handleDateClick,
        eventClick: this.handleEventClick,
        // eventsSet: this.handleEvents,
        select: this.handleSelect,
        resourceAreaColumns: [
          {
            headerContent: "会议室",
          },
        ],
        eventColor: "#f08f00", // 修改日程背景色
        locale: "zh-cn", // 设置语言
        weekNumberCalculation: "ISO", // 周数
        customButtons: {
          prev: {
            // this overrides the prev button
            text: "PREV",
            click: () => {
              this.prev();
            },
          },
          next: {
            // this overrides the next button
            text: "PREV",
            click: () => {
              this.next();
            },
          },
          today: {
            text: "今天",
            click: () => {
              this.today();
            },
          },
        },
        // 最小时间
        slotMinTime: "07:00:00",
        // 最大时间
        slotMaxTime: "22:00:00",
        resourceAreaWidth: "15%", //高级功能配置Column宽度
        // 高级功能许可key,需要用到高级功能时候添加上,避免左下角出现警告提醒
        schedulerLicenseKey: "CC-Attribution-NonCommercial-NoDerivatives",
      },
    };
  },
  created() {
    this.getList();
  },
  mounted() {
    this.initCalendar();
  },
  methods: {
    /** 查询会议室列表 (替换为自己本公司的接口)*/
    getList() {
      this.queryParams.companyIds = 1319;
      this.queryParams.viewType = "day";
      this.queryParams.meetingDay = this.setCurrentDate(new Date());
      this.getListApplyMeetingroom(this.queryParams);
    },
    ///获取当前的年月日,做查询参数
    setCurrentDate(data) {
      const currentDate = data;
      const year = currentDate.getFullYear();
      const month = String(currentDate.getMonth() + 1).padStart(2, "0");
      const day = String(currentDate.getDate()).padStart(2, "0");
      const formattedDate = `${year}-${month}-${day}`;
      return formattedDate;
    },
    getListApplyMeetingroom(queryParams) {
      this.$nextTick(() => {
        // listApplyMeetingroom(queryParams).then((response) => {}); //修改为自己的接口

        //清空原始数据
        this.meetingroomList = [
          {
            id: 1,
            companyId: 1319,
            companyName: "测试",
            companyShortName: null,
            tenantId: 1319,
            meetingroomNo: "HYS20241118001",
            meetingroomName: "测试 - 309会议室",
            meetingroomLocation: "测试公司三层309会议室",
            meetingroomArea: "86.0000",
            meetingroomGalleryful: 40,
            meetingroomEquipment: "投影仪",
            appliable: 1,
            remark: "第一次申请",
            scopeDeptId: 1321,
            projectionScreen: null,
            tableBrand: null,
            bottleWater: null,
          },
        ];
        this.meetingroomEventList = [
          {
            id: 20,
            meetingroomId: 1,
            meetingroomName: "测试 - 309会议室",
            meetingName: "22好的会议",
            participant: "我额人他",
            meetingStartTime: "2024-11-22 09:30:00",
            meetingEndTime: "2024-11-22 13:30:00",
            specialRequest: null,
            companyId: 1319,
            companyName: "测试公司",
            userCode: "1",
            userName: "sysadmin",
            userPhone: "1222222222",
            deptId: 1323,
            deptName: "研发部",
            applyTime: null,
            applyStatus: 1,
            cancelRemark: null,
            remark: "问问嗯嗯",
            tenantId: 1319,
            scopeDeptId: 1323,
            projectionScreen: 1,
            tableBrand: 1,
            bottleWater: 1,
            otherNeeds: "问问",
          },
        ];

        // 提取所有资源的 id 值
        const resourceIds = this.calendar
          .getResources()
          .map((resource) => resource.id);
        // 逐个删除原有资源,防止显示出错
        resourceIds.forEach((id) => {
          this.calendar.getResourceById(id).remove();
        });

        if (this.meetingroomList.length > 0) {
          // 遍历 this.meetingroomList 并添加资源
          this.meetingroomList.forEach((room) => {
            this.calendar.addResource({
              id: room.id,
              title: room.meetingroomName,
            });
          });
        }

        if (this.meetingroomEventList.length > 0) {
          // 获取现有的事件列表
          const existingEvents = this.calendar.getEvents();
          // 添加新的事件,避免重复
          this.meetingroomEventList.forEach((event) => {
            const isDuplicate = existingEvents.some((existingEvent) => {
              const formattedStart = this.formmatTime(existingEvent.start);
              const formattedEnd = this.formmatTime(existingEvent.end);

              return (
                formattedStart === this.formmatTime(event.meetingStartTime) &&
                formattedEnd === this.formmatTime(event.meetingEndTime)
              );
            });

            if (!isDuplicate) {
              this.calendar.addEvent({
                resourceId: event.meetingroomId,
                title: `${event.meetingName}  预定人: (${event.userName})`,
                start: event.meetingStartTime,
                end: event.meetingEndTime,
                extendedProps: {
                  meetingroomId: event.meetingroomId,
                  meetingroomName: event.meetingroomName,
                  meetingStartTime: event.meetingStartTime,
                  meetingEndTime: event.meetingEndTime,
                  meetingName: event.meetingName,
                  userName: event.userName,
                  userPhone: event.userPhone,
                  companyName: event.companyName,
                  deptName: event.deptName,
                  participant: event.participant,
                  projectionScreen: event.projectionScreen,
                  tableBrand: event.tableBrand,
                  bottleWater: event.bottleWater,
                  otherNeeds: event.otherNeeds,
                  remark: event.remark,
                },
              });
            }
          });
        }
      });
    },
    //加载会议事件
    initCalendar() {
      var calendarEl = document.getElementById("calendar");
      this.calendar = new Calendar(calendarEl, this.calendarOptions);
      this.calendar.render();
    },
    openDialog() {
      this.resetForm();
      this.title = "会议室预定";
      this.form.meetingStartTime = "";
      this.form.meetingEndTime = "";
      this.form.meetingroomName = "";
      this.form.meetingroomId = "";
      this.eventClickFlag = false;

      this.dialogVisible = true;
    },
    roomChange(value) {
      // 根据选中的会议室名称找到对应的会议室对象
      const selectedRoom = this.meetingroomList.find(
        (room) => room.meetingroomName === value
      );
      if (selectedRoom) {
        // 更新 form.meetingroomId 为选中的会议室meetingroomNo
        this.form.meetingroomId = selectedRoom.id;
      }
    },
    //选择会议室和时间
    handleSelect(info) {
      this.resetForm();
      this.form.meetingStartTime = this.handleSelectDate(info.startStr);
      this.form.meetingEndTime = this.handleSelectDate(info.endStr);
      if (info.resource) {
        this.form.meetingroomName = info.resource.title;
        this.form.meetingroomId = info.resource.id;
      }
      this.eventClickFlag = false;
      this.dialogVisible = true;
    },
    handleSelectDate(selectData) {
      const originalTime = selectData;
      const date = new Date(originalTime);
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      const hours = String(date.getHours()).padStart(2, "0");
      const minutes = String(date.getMinutes()).padStart(2, "0");
      const formattedTime = `${year}-${month}-${day} ${hours}:${minutes}:00`;
      return formattedTime;
    },
    resetForm() {
      this.form.meetingName = "";
      this.form.participant = "";
      this.form.projectionScreen = "";
      this.form.tableBrand = "";
      this.form.bottleWater = "";
      this.form.otherNeeds = "";
      this.form.remark = "";
    },
    //获取视图的日期参数
    getViewDate() {
      const currentDate = this.calendar.getDate();
      const currentViewType = this.calendar.view.type;

      if (currentViewType === "resourceTimelineDay") {
        // 日视图
        this.queryParams.viewType = "day";
        this.queryParams.meetingDay = this.formatDate(
          currentDate,
          "yyyy-MM-dd"
        );
      } else if (currentViewType === "resourceTimelineWeek") {
        // 周视图
        const startOfWeek = new Date(currentDate);
        startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay() + 1);
        const endOfWeek = new Date(startOfWeek);
        endOfWeek.setDate(endOfWeek.getDate() + 6);
        this.queryParams.viewType = "week";
        this.queryParams.beginDayTime = this.formatDate(
          startOfWeek,
          "yyyy-MM-dd"
        );
        this.queryParams.endDayTime = this.formatDate(endOfWeek, "yyyy-MM-dd");
      } else if (currentViewType === "dayGridMonth") {
        // 月视图

        const startOfMonth = new Date(currentDate);
        startOfMonth.setDate(1);
        const endOfMonth = new Date(currentDate);
        endOfMonth.setMonth(endOfMonth.getMonth() + 1);
        endOfMonth.setDate(0);
        this.queryParams.viewType = "month";
        this.queryParams.beginDayTime = this.formatDate(
          startOfMonth,
          "yyyy-MM-dd"
        );
        this.queryParams.endDayTime = this.formatDate(endOfMonth, "yyyy-MM-dd");
      }
      this.queryParams.companyIds =
        this.queryParams.tenantIdList.length > 0
          ? this.queryParams.tenantIdList.join(",")
          : this.form.companyId;
    },
    // 辅助方法:格式化日期
    formatDate(date, format) {
      const pad = (n) => (n < 10 ? "0" + n : n);
      return format
        .replace("yyyy", date.getFullYear())
        .replace("MM", pad(date.getMonth() + 1))
        .replace("dd", pad(date.getDate()));
    },
    prev() {
      this.calendar.prev();
      this.getViewDate();
      this.getListApplyMeetingroom(this.queryParams);
    },
    // 切换下一个按钮事件
    next() {
      this.calendar.next();
      this.getViewDate();
      this.getListApplyMeetingroom(this.queryParams);
    },
    // 点击今天按钮
    today() {
      this.calendar.today();
      this.getViewDate();
      this.getListApplyMeetingroom(this.queryParams);
    },
    handleDateClick: function (arg) {
      this.$forceUpdate();
      console.log(arg, "事件1");
    },
    handleEventClick(calEvent) {
      console.log(calEvent, "事件2");
      this.title = "查看会议详情";
      // 将 extendedProps 里的字段及数值逐个放入 this.form
      const extendedProps = calEvent.event.extendedProps;
      for (const key in extendedProps) {
        if (extendedProps.hasOwnProperty(key)) {
          this.form[key] = extendedProps[key];
        }
      }
      this.eventClickFlag = true;
      this.dialogVisible = true; // 显示dialog弹窗
    },
    getShowTime(beginDate, endDate) {
      this.form.startDate = this.dealWithTime(beginDate);
      this.form.startTime = this.getHoursMin(beginDate);
      this.form.endDate = this.dealWithTime(endDate);
      this.form.endTime = this.getHoursMin(endDate);
    },
    // 获取时分时间
    getHoursMin(value) {
      return value.substring(11, 16);
    },
    // 处理会议时间格式
    dealWithTime(date) {
      let newDate = /\d{4}-\d{1,2}-\d{1,2}/g.exec(date)[0];
      return newDate;
    },
    handleEvents(events) {
      console.log(events, "事件3");
    },
    getReservationList(arrayData) {
      let newArr = [];
      this.subList = arrayData;
      arrayData.forEach((item) => {
        newArr.push({
          start: this.dealWithTime(item.beginDate),
          end: this.addDate(this.dealWithTime(item.endDate), 1),
          color: item.status,
          id: item.id,
          title: `${this.getTitle(item.beginDate, item.endDate)} ${item.title}`,
        });
      });
      this.calendarOptions.events = newArr;
    },
    //UTC时间去掉T
    formmatTime(time) {
      const utcTimestamp = time;
      const date = new Date(utcTimestamp);

      const year = date.getUTCFullYear();
      const month = String(date.getUTCMonth() + 1).padStart(2, "0");
      const day = String(date.getUTCDate()).padStart(2, "0");

      const hours = String(date.getUTCHours()).padStart(2, "0");
      const minutes = String(date.getUTCMinutes()).padStart(2, "0");
      const seconds = String(date.getUTCSeconds()).padStart(2, "0");

      const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;

      return formattedDateTime;
    },

    datesSet(info) {
      //注意:该方法在页面初始化时就会触发一次
      this.viewType = info.view.type;
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate((valid) => {
        if (valid) {
        }
      });
    },
  },
};
</script>
<style scoped lang="scss">
::v-deep .el-select {
  width: 100%;
}
::v-deep .el-date-editor.el-input,
.el-date-editor.el-input__inner {
  width: 100%;
}
</style>

http://www.kler.cn/a/405434.html

相关文章:

  • c语言学习23数组传递到子函数
  • 深度解析神经网络中的最大池化层:工作原理、参数配置与应用示例
  • Java中的TreeSet集合解析
  • Tailscale 自建 Derp 中转服务器(全程无 Docker + 无域名纯 IP 版本)
  • 测试工程师如何在面试中脱颖而出
  • 图形学笔记 - 4. 几何 -网格操作和阴影映射
  • VMware虚拟机Ubuntu桥接模式突然连接不上网络解决办法
  • 类文件结构详解.上
  • Linux-Apache静态资源
  • 【SpringBoot】发送各种复杂格式的邮件
  • Centos 8, add repo
  • .net 8使用hangfire实现库存同步任务
  • 分布式锁RedissonClient应用
  • 某车企ASW面试笔试题
  • Linux tcpdump 详解教程
  • 海盗王集成网关和商城服务端功能golang版
  • 重构代码之引入本地扩展
  • 【IOS】编译缓存错误Library/Caches/com.apple.mobile.installd.staging
  • 直流电表精准计量,为光伏产业续航
  • 2025蓝桥杯(单片机)备赛--扩展外设之UART1的原理与应用(十二)
  • 分治法的魅力:高效解决复杂问题的利器
  • 什么是axios?怎么使用axios封装Ajax?
  • 第1章 初识SpringMVC
  • 【滑动窗口】至少有k个重复字符的最长子串
  • 系统思考—跳出症状看全局
  • 【linux013】文件操作命令篇 - less 命令