Vue中FullCalendar按日期动态切换资源的问题求助
问题描述
我在Vue项目中使用FullCalendar的resourceTimeGridDay视图,需要根据日期动态切换资源——仅显示当日可用的员工(通过接口获取当日非休假的员工数据)。
使用eventDidMount钩子时,仅当日存在事件时能正确切换资源,无事件则不生效;尝试使用datesSet钩子时,出现错误:Pickup.vue:97 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'calendarOptions')。
后端代码(PickupController.php)
public function getResources(Request $request){ /** * TODO * * SELECT * employees.id * FROM employees * left JOIN appointments * ON appointments.employee_id = employees.id * WHERE employees.id NOT IN ( * SELECT appointments.employee_id WHERE (date(appointments.start_time) <= CURDATE() AND date(appointments.finish_time) >= CURDATE())) * AND employees.department_id = 3 * GROUP BY employees.id */ $employees = Employee::select('employees.id','employees.name','employees.business_start_time','employees.business_finish_time') ->leftjoin('appointments','employees.id','=','appointments.employee_id') ->whereNotIn('employees.id', DB::table('appointments')->select('appointments.employee_id')->whereDate(('appointments.start_time'),'<=',$request->date)->whereDate(('appointments.finish_time'),'>=',$request->date)) ->where('employees.department_id','=',3) ->groupBy('employees.id') ->get(); $resources = []; foreach($employees as $employee) { $resource = []; $resource['id'] = $employee->id; $resource['title'] = $employee->name; $resource['businessHours']['daysOfWeek'] = [ 1, 2, 3, 4, 5]; $resource['businessHours']['startTime'] = $employee->business_start_time; $resource['businessHours']['endTime'] = $employee->business_finish_time; $resources[] = $resource; } return response()->json($resources); }
前端代码(Pickup.vue)
<script> import FullCalendar from '@fullcalendar/vue3' import timeGridPlugin from "@fullcalendar/timegrid" import dayGridPlugin from '@fullcalendar/daygrid' import interactionPlugin from '@fullcalendar/interaction' import resourceTimeGridPlugin from '@fullcalendar/resource-timegrid' import scrollGridPlugin from '@fullcalendar/scrollgrid' import listPlugin from '@fullcalendar/list' import esLocale from '@fullcalendar/core/locales/es' import adaptivePlugin from '@fullcalendar/adaptive' import PickupModal from '../../Components/Modals/PickupModal.vue' import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue' import axios from 'axios' import { router } from "@inertiajs/vue3" import moment from 'moment' import { reactive } from "vue"; export default { name: 'Pickup', components: { FullCalendar, PickupModal, AuthenticatedLayout }, emits: { 'deleteEventResource':{ id:'', }, 'editEventResource':{ id:'', }, 'closeModal':true, 'saveEventResource':{id:'',}, }, props: { errors: Object, events: Array, resources: Array, }, data() { return { showModal: false, editing : false, newEventResource: { title: '', content: '', date_at: '', end_at: '', status: '', color: '', content: '' }, calendarOptions : { schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source', plugins: [ dayGridPlugin, timeGridPlugin, listPlugin,interactionPlugin, adaptivePlugin, resourceTimeGridPlugin, scrollGridPlugin ], headerToolbar: { left: "prev,next today", center: "title", right: "dayGridMonth,timeGridWeek,resourceTimeGridDay,listMonth", }, initialView: 'resourceTimeGridDay', locale: esLocale, dayMaxEvents: false, weekends: true, navLinks: true, nowIndicator: true, firstDay: 1, defaultAllDay: false, timeZone: 'Europe/Madrid', resources: [], dateClick: this.handleDateClick, eventClick: this.handleEventClick, events: [], dayMinWidth: 150, stickyFooterScrollbar: true, businessHours: [], slotDuration: '00:15:00', eventContent: function(eventInfo) { return { html: eventInfo.timeText + "<br>" + eventInfo.event.title + "<br>" + eventInfo.event.extendedProps.content } }, eventDidMount: this.dayDrivers, //仅当日有事件时生效,资源能切换 eventMouseEnter: function(info) {info.el.title = info.event.extendedProps.content;}, datesSet:function (dateInfo){ let date = dateInfo.startStr.split("T")[0]; axios.get(route('pickups.index',{date:date})).then(({data}) => { this.$data.calendarOptions.resources = data; //报错:Cannot read properties of undefined (reading 'calendarOptions') }); }, //lazyFetching: false, //rerenderDelay: 500, } } }, beforeMount(){ const start = new Date().toISOString().split("T")[0]; this.$data.calendarOptions.resources = { url: route('pickups.index',{date:start}), method: 'GET', failure: error => { console.log('tenemos este error: ', error.message); } }, this.$data.calendarOptions.events = { url: route('pickupsEvents'), method: 'GET', failure: error => { console.log('tenemos este error: ', error.message); } } }, methods: { dateClick(arg){ this.$data.showModal = true; this.setModalOpen(arg); }, handleDateClick(clickInfo){ this.resetModal(); this.$data.showModal = true; this.$emit('dateClick',clickInfo); }, dayDrivers(date){ let calendarApi = this.$refs.fullCalendar.getApi() const start = calendarApi.getDate().toISOString().split("T")[0]; axios.get(route('pickups.index',{date:start})).then(({data}) => { //calendarApi.addResource(data); this.$data.calendarOptions.resources = data; //this.$refs.fullCalendar.getApi().refetchResources(); }); }, </script>
解决方案
1. 修复datesSet钩子的this指向问题
datesSet中使用普通函数时,this的指向不是Vue组件实例,导致无法访问this.$data.calendarOptions。改成箭头函数即可解决:
datesSet: (dateInfo) => { let date = dateInfo.startStr.split("T")[0]; axios.get(route('pickups.index',{date:date})).then(({data}) => { this.$data.calendarOptions.resources = data; }); }
2. 推荐使用FullCalendar API更新资源(更可靠)
直接修改calendarOptions.resources可能不会触发日历的重新渲染,建议通过FullCalendar提供的API来操作资源:
datesSet: (dateInfo) => { const calendarApi = dateInfo.view.calendar; // 获取日历实例 const date = dateInfo.startStr.split("T")[0]; axios.get(route('pickups.index',{date:date})).then(({data}) => { calendarApi.removeAllResources(); // 清空现有资源 data.forEach(resource => { calendarApi.addResource(resource); // 添加新资源 }); }); }
3. 替换eventDidMount为datesSet(覆盖所有日期场景)
eventDidMount只有在当天存在事件时才会触发,无法覆盖无事件的日期。使用datesSet钩子,只要日期切换就会触发,完美适配所有场景。
内容的提问来源于stack exchange,提问作者vuenew
相关产品推荐
相关产品推荐

