Vue3+FullCalendar实战:手把手教你打造企业级排班系统(附完整代码)

张开发
2026/6/9 18:51:58 15 分钟阅读
Vue3+FullCalendar实战:手把手教你打造企业级排班系统(附完整代码)
Vue3FullCalendar实战从零构建企业级智能排班系统企业排班管理一直是人力资源和项目管理中的核心痛点。传统表格工具难以直观展示复杂的轮班规则、调休安排和人员分布而市面上的SaaS系统又往往缺乏定制灵活性。本文将带你用Vue3和FullCalendar打造一个高度定制化的排班解决方案包含以下特色功能可视化拖拽排班像安排会议一样轻松调整班次多维度视图切换支持月/周/日视图和资源时间轴智能冲突检测自动识别重复排班和超时工作实时数据同步与后端API无缝对接1. 环境搭建与基础配置首先创建Vue3项目并安装必要依赖npm create vuelatest shift-schedule-system cd shift-schedule-system npm install fullcalendar/vue3 fullcalendar/core fullcalendar/daygrid fullcalendar/timegrid fullcalendar/interaction fullcalendar/resource-timeline基础日历组件配置template FullCalendar :optionscalendarOptions / /template script setup import { ref } from vue import FullCalendar from fullcalendar/vue3 import dayGridPlugin from fullcalendar/daygrid import timeGridPlugin from fullcalendar/timegrid import interactionPlugin from fullcalendar/interaction import resourceTimelinePlugin from fullcalendar/resource-timeline const calendarOptions ref({ plugins: [ dayGridPlugin, timeGridPlugin, interactionPlugin, resourceTimelinePlugin ], initialView: resourceTimelineWeek, headerToolbar: { left: prev,next today, center: title, right: dayGridMonth,timeGridWeek,resourceTimelineDay }, editable: true, selectable: true }) /script关键配置参数说明参数类型说明initialViewstring默认视图模式editableboolean是否允许事件编辑selectableboolean是否允许选择时间段selectMirrorboolean选择时显示半透明副本2. 资源与事件数据建模企业排班通常涉及三个核心数据实体资源(Resources)员工、设备或场地事件(Events)具体的班次安排规则(Rules)排班约束条件interface ShiftEvent { id: string title: string start: Date end: Date resourceId: string extendedProps: { shiftType: 早班 | 中班 | 晚班 status: confirmed | pending } } interface StaffResource { id: string title: string department: string color?: string }数据初始化示例const resources ref([ { id: 1, title: 张三, department: 研发部 }, { id: 2, title: 李四, department: 市场部 } ]) const events ref([ { id: 101, title: 早班, start: 2024-06-01T09:00:00, end: 2024-06-01T17:00:00, resourceId: 1, extendedProps: { shiftType: 早班, status: confirmed } } ])3. 高级交互功能实现3.1 拖拽排班与冲突检测const handleEventDrop (info) { const overlappingEvents calendar.value.getApi().getEvents().filter( event event.id ! info.event.id event.resourceId info.event.resourceId ( (info.event.start event.end info.event.end event.start) ) ) if (overlappingEvents.length 0) { info.revert() ElMessage.error(该时段已有排班安排) } else { // 调用API更新后端数据 updateShiftSchedule(info.event) } }3.2 批量排班功能const batchCreateShifts (staffIds, shiftTemplate) { const newEvents staffIds.map(staffId { return { ...shiftTemplate, id: generateId(), resourceId: staffId } }) calendar.value.getApi().addEventSource(newEvents) }3.3 自定义渲染与样式通过eventContent钩子自定义事件显示const calendarOptions ref({ // ...其他配置 eventContent: (arg) { const shiftType arg.event.extendedProps.shiftType const colorMap { 早班: #FFEEBA, 中班: #B8E0D2, 晚班: #EAC4D5 } return { html: div classcustom-event stylebackground:${colorMap[shiftType]} div classevent-title${arg.event.title}/div div classevent-time${formatTime(arg.event.start)}-${formatTime(arg.event.end)}/div /div } } })4. 后端集成与性能优化4.1 分页加载策略const handleDatesSet (arg) { loadResources(arg.start, arg.end) loadEvents(arg.start, arg.end) } const loadEvents async (start, end) { const { data } await api.get(/shifts, { params: { start: formatISO(start), end: formatISO(end), page: 1, pageSize: 100 } }) calendar.value.getApi().removeAllEvents() calendar.value.getApi().addEventSource(data.items) }4.2 WebSocket实时更新import { io } from socket.io-client const socket io(https://api.example.com) onMounted(() { socket.on(shift-updated, (event) { const existingEvent calendar.value.getApi().getEventById(event.id) if (existingEvent) { existingEvent.setProp(title, event.title) existingEvent.setDates(event.start, event.end) existingEvent.setExtendedProp(status, event.status) } }) })5. 企业级功能扩展5.1 排班规则引擎const validateShift (event) { // 检查最小休息时间 const prevShifts getPreviousShifts(event.resourceId, event.start) if (prevShifts.some(shift { return diffHours(shift.end, event.start) 12 })) { return { valid: false, message: 员工必须有至少12小时休息时间 } } // 检查每月工时上限 const monthlyHours getMonthlyHours(event.resourceId, event.start) if (monthlyHours event.duration 176) { return { valid: false, message: 本月工时已超过176小时上限 } } return { valid: true } }5.2 导出与打印优化const exportToPDF () { const calendarApi calendar.value.getApi() const view calendarApi.view const title ${view.title}排班表 html2canvas(document.querySelector(.fc-view)).then(canvas { const pdf new jsPDF(landscape) pdf.addImage(canvas.toDataURL(image/png), PNG, 10, 10, 280, 150) pdf.save(${title}.pdf) }) }6. 移动端适配技巧针对移动设备的响应式配置const isMobile ref(window.innerWidth 768) const updateViewType () { calendarOptions.value.headerToolbar isMobile.value ? { left: prev,next, center: title, right: } : { left: prev,next today, center: title, right: dayGridMonth,timeGridWeek,resourceTimelineDay } calendarOptions.value.initialView isMobile.value ? timeGridDay : resourceTimelineWeek } onMounted(() { window.addEventListener(resize, () { isMobile.value window.innerWidth 768 updateViewType() }) })在最近为某连锁零售企业实施排班系统时我们发现资源时间轴视图在平板设备上表现最佳既保持了足够的操作空间又能清晰展示多人员排班情况。通过监听touch事件实现了原生的拖拽体验大幅减少了移动端用户的学习成本。

更多文章