基于JS实现的员工排班表(附源码)
演示效果

HTML 代码
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>员工排班系统</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <link href="css.css" rel="stylesheet" /></head><body> <div class="container"> <header> <h1><i class="fas fa-calendar-alt"></i>员工排班系统</h1> <p class="header-subtitle">高效管理团队工作时间,优化人力资源分配</p> </header> <div class="controls"> <div class="date-navigation"><button id="prev-week" class="btn btn-circle"><i class="fas fa-chevron-left"></i></button> <div class="current-week" id="current-week">2023年11月6日 - 2023年11月12日</div><button id="next-week" class="btn btn-circle"><i class="fas fa-chevron-right"></i></button> </div><button id="add-employee" class="btn btn-primary"><i class="fas fa-user-plus"></i>添加员工</button> </div> <div class="schedule-container"> <div class="employee-list" id="employee-list"> <h3><i class="fas fa-users"></i>员工列表</h3> <div id="employee-items-container"></div> </div> <div class="schedule-grid"> <div class="schedule-header" id="schedule-header"></div> <div class="schedule-body" id="schedule-body"></div> </div> </div> </div><!-- 添加/编辑班次模态框 --> <div id="shift-modal" class="modal"> <div class="modal-content"><span class="close">×</span> <div class="modal-header"> <h2><i class="fas fa-calendar-plus"></i><span id="modal-title">添加班次</span></h2> </div> <form id="shift-form"><input type="hidden" id="shift-id"> <div class="form-group"><label for="shift-employee"><i class="fas fa-user"></i>员工</label><select id="shift-employee" required> <option value="">选择员工</option> </select></div> <div class="form-group"><label for="shift-type"><i class="fas fa-clock"></i>班次类型</label><select id="shift-type" required> <option value="">选择班次类型</option> <option value="morning">早班(8:00-16:00)</option> <option value="evening">晚班(16:00-24:00)</option> <option value="night">夜班(0:00-8:00)</option> <option value="custom">自定义时间</option> </select></div> <div id="custom-time-container" style="display: none;"> <div class="form-group"><label for="shift-start">开始时间</label><input type="time" id="shift-start"> </div> <div class="form-group"><label for="shift-end">结束时间</label><input type="time" id="shift-end"></div> </div> <div class="form-group"><label for="shift-date"><i class="fas fa-calendar-day"></i>日期</label><input type="date" id="shift-date" required></div> <div class="form-group"><label for="shift-notes"><i class="fas fa-sticky-note"></i>备注</label><textarea id="shift-notes" rows="3" placeholder="可添加班次备注信息"></textarea></div> <div class="form-actions"><button type="button" id="delete-shift" class="btn btn-danger" style="display: none;"><i class="fas fa-trash"></i>删除</button><button type="submit" class="btn btn-primary"><i class="fas fa-save"></i>保存</button></div> </form> </div> </div><!-- 添加员工模态框 --> <div id="employee-modal" class="modal"> <div class="modal-content"><span class="close">×</span> <div class="modal-header"> <h2><i class="fas fa-user-plus"></i>添加新员工</h2> </div> <form id="employee-form"> <div class="form-group"><label for="employee-name"><i class="fas fa-signature"></i>姓名</label><input type="text" id="employee-name" required></div> <div class="form-group"><label for="employee-position"><i class="fas fa-briefcase"></i>职位</label><input type="text" id="employee-position"></div> <div class="form-group"><label for="employee-phone"><i class="fas fa-phone"></i>电话</label><input type="tel" id="employee-phone"></div> <div class="form-group"><label for="employee-email"><i class="fas fa-envelope"></i>邮箱</label><input type="email" id="employee-email"></div> <div class="form-actions"><button type="submit" class="btn btn-primary"><i class="fas fa-plus"></i>添加员工</button></div> </form> </div> </div> <script src="js.js"></script></body></html>
CSS 代码
:root { --primary-color: #4361ee; --primary-light: #4895ef; --secondary-color: #3f37c9; --accent-color: #4cc9f0; --success-color: #4ade80; --warning-color: #fbbf24; --danger-color: #f87171; --light-color: #f8f9fa; --dark-color: #212529; --gray-color: #6c757d; --border-radius: 12px; --box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);}* { box-sizing: border-box; margin: 0; padding: 0;}body { font-family: "Noto Sans SC", sans-serif; background-color: #f1f5f9; color: var(--dark-color); line-height: 1.6; padding: 20px;}.container { max-width: 1400px; margin: 0 auto; background-color: white; border-radius: var(--border-radius); box-shadow: var(--box-shadow); overflow: hidden;}header { background: linear-gradient( 135deg, var(--primary-color), var(--secondary-color) ); color: white; padding: 25px 30px; position: relative; overflow: hidden;}header::before { content: ""; position: absolute; top: -50px; right: -50px; width: 200px; height: 200px; background: rgba(255, 255, 255, 0.1); border-radius: 50%;}header::after { content: ""; position: absolute; bottom: -80px; right: -30px; width: 300px; height: 300px; background: rgba(255, 255, 255, 0.05); border-radius: 50%;}h1 { font-weight: 700; font-size: 28px; margin-bottom: 5px; position: relative; z-index: 1;}.header-subtitle { font-weight: 300; opacity: 0.9; font-size: 16px; position: relative; z-index: 1;}.controls { display: flex; justify-content: space-between; align-items: center; padding: 20px 30px; background-color: white; border-bottom: 1px solid rgba(0, 0, 0, 0.05);}.date-navigation { display: flex; align-items: center; gap: 10px;}.btn { padding: 10px 20px; border-radius: 50px; border: 0; font-weight: 500; cursor: pointer; transition: var(--transition); display: inline-flex; align-items: center; gap: 8px;}.btn i { font-size: 14px;}.btn-primary { background-color: var(--primary-color); color: white;}.btn-primary:hover { background-color: var(--secondary-color); transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);}.btn-outline { background-color: transparent; border: 1px solid var(--gray-color); color: var(--gray-color);}.btn-outline:hover { border-color: var(--primary-color); color: var(--primary-color);}.btn-circle { width: 40px; height: 40px; border-radius: 50%; padding: 0; justify-content: center; background-color: white; border: 1px solid #e2e8f0; color: var(--gray-color);}.btn-circle:hover { background-color: var(--primary-color); color: white; border-color: var(--primary-color);}.current-week { font-weight: 500; min-width: 250px; text-align: center; font-size: 16px; color: var(--dark-color);}.schedule-container { display: flex; min-height: 600px;}.employee-list { width: 250px; border-right: 1px solid #e2e8f0; padding: 20px; background-color: #f8fafc;}.employee-list h3 { font-size: 18px; margin-bottom: 20px; color: var(--dark-color); display: flex; align-items: center; gap: 10px;}.employee-list h3 i { color: var(--primary-color);}.employee-item { padding: 15px; margin-bottom: 10px; background-color: white; border-radius: var(--border-radius); cursor: pointer; transition: var(--transition); border-left: 4px solid var(--primary-color); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); display: flex; align-items: center; gap: 10px;}.employee-item:hover { transform: translateX(5px); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);}.employee-avatar { width: 36px; height: 36px; border-radius: 50%; background-color: var(--primary-light); display: flex; align-items: center; justify-content: center; color: white; font-weight: 500;}.employee-info { flex: 1;}.employee-name { font-weight: 500; margin-bottom: 2px;}.employee-position { font-size: 12px; color: var(--gray-color);}.schedule-grid { flex: 1; overflow-x: auto;}.schedule-header { display: grid; grid-template-columns: repeat(7, minmax(130px, 1fr)); background-color: white; color: var(--dark-color); text-align: center; font-weight: 500; border-bottom: 1px solid #e2e8f0;}.schedule-header div { padding: 15px 10px; border-right: 1px solid #e2e8f0; transition: var(--transition);}.schedule-header div:hover { background-color: #f8fafc;}.schedule-header div:last-child { border-right: 0;}.day-name { font-size: 16px; margin-bottom: 5px;}.day-date { font-size: 14px; color: var(--gray-color);}.schedule-body { display: grid; grid-template-columns: repeat(7, minmax(130px, 1fr)); background-color: #f8fafc;}.day-column { min-height: 500px; border-right: 1px solid #e2e8f0; padding: 10px; transition: var(--transition); position: relative;}.day-column:hover { background-color: rgba(255, 255, 255, 0.7);}.day-column:last-child { border-right: 0;}.day-column.today { background-color: rgba(67, 97, 238, 0.05);}.day-column.today::after { content: "今天"; position: absolute; top: 10px; right: 10px; background-color: var(--primary-color); color: white; font-size: 12px; padding: 2px 8px; border-radius: 10px;}.shift-slot { background-color: white; border-radius: var(--border-radius); padding: 12px; margin-bottom: 10px; cursor: pointer; transition: var(--transition); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); border-left: 4px solid var(--primary-color);}.shift-slot:hover { transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);}.shift-morning { border-left-color: var(--success-color);}.shift-evening { border-left-color: var(--warning-color);}.shift-night { border-left-color: var(--secondary-color);}.shift-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;}.shift-type { font-weight: 500; font-size: 14px; color: var(--gray-color);}.shift-time { font-size: 12px; background-color: #f1f5f9; padding: 2px 6px; border-radius: 4px; color: var(--gray-color);}.shift-employee { display: flex; align-items: center; gap: 8px;}.shift-avatar { width: 28px; height: 28px; border-radius: 50%; background-color: var(--primary-light); display: flex; align-items: center; justify-content: center; color: white; font-size: 12px; font-weight: 500;}.shift-notes { font-size: 12px; color: var(--gray-color); margin-top: 8px; padding-top: 8px; border-top: 1px dashed #e2e8f0;}.empty-state { text-align: center; padding: 20px 10px; color: var(--gray-color); display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100px;}.empty-state i { font-size: 24px; margin-bottom: 10px; opacity: 0.5;}.empty-state p { margin-bottom: 10px; font-size: 12px;}.empty-state button { padding: 6px 12px; font-size: 12px;}.modal { display: none; position: fixed; z-index: 100; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(5px); animation: fadeIn 0.3s ease;}@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; }}.modal-content { background-color: white; margin: 5% auto; padding: 30px; border-radius: var(--border-radius); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); width: 90%; max-width: 500px; animation: slideDown 0.4s ease; position: relative; overflow: hidden;}@keyframes slideDown { from { transform: translateY(-50px); opacity: 0; } to { transform: translateY(0); opacity: 1; }}.modal-header { margin-bottom: 25px; position: relative;}.modal-header h2 { font-size: 24px; color: var(--dark-color); display: flex; align-items: center; gap: 10px;}.modal-header h2 i { color: var(--primary-color);}.close { position: absolute; top: 10px; right: 10px; width: 36px; height: 36px; background-color: #f1f5f9; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--transition); color: var(--gray-color); font-size: 18px;}.close:hover { background-color: var(--danger-color); color: white; transform: rotate(90deg);}.form-group { margin-bottom: 20px;}label { display: block; margin-bottom: 8px; font-weight: 500; color: var(--dark-color); font-size: 14px;}select,input,textarea { width: 100%; padding: 12px 15px; border: 1px solid #e2e8f0; border-radius: var(--border-radius); font-family: inherit; transition: var(--transition); background-color: white;}select:focus,input:focus,textarea:focus { outline: 0; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);}.form-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 30px;}.btn-danger { background-color: var(--danger-color); color: white;}.btn-danger:hover { background-color: #dc2626;}@media (max-width: 992px) { .schedule-container { flex-direction: column; } .employee-list { width: 100%; border-right: 0; border-bottom: 1px solid #e2e8f0; } .schedule-header { grid-template-columns: repeat(7, minmax(120px, 1fr)); } .schedule-body { grid-template-columns: repeat(7, minmax(120px, 1fr)); }}@media (max-width: 768px) { .controls { flex-direction: column; gap: 15px; align-items: flex-start; } .date-navigation { width: 100%; justify-content: space-between; } .modal-content { margin: 20px auto; padding: 20px; } .schedule-header { grid-template-columns: repeat(7, minmax(80px, 1fr)); } .schedule-body { grid-template-columns: repeat(7, minmax(80px, 1fr)); }}
JS 代码
// 员工数据const employees = [ { id: '1', name: '张三', position: '高级工程师' }, { id: '2', name: '李四', position: '产品经理' }, { id: '3', name: '王五', position: '设计师' }, { id: '4', name: '赵六', position: '实习生' }, { id: '5', name: '钱七', position: '测试工程师' }];// 班次类型数据const shiftTypes = [ { name: '早班', value: 'morning', time: '8:00 - 16:00' }, { name: '晚班', value: 'evening', time: '16:00 - 24:00' }, { name: '夜班', value: 'night', time: '0:00 - 8:00' }];// 班次数据const shifts = [ { id: '1', day: 1, employeeId: '1', type: 'morning', notes: '负责前端开发工作' }, { id: '2', day: 1, employeeId: '2', type: 'evening', notes: '' }, { id: '3', day: 2, employeeId: '3', type: 'morning', notes: '' }, { id: '4', day: 2, employeeId: '4', type: 'night', notes: '需要交接给早班' }, { id: '5', day: 3, employeeId: '5', type: 'morning', notes: '' }];// 当前显示的周let currentWeekStart = new Date();currentWeekStart.setDate(currentWeekStart.getDate() - currentWeekStart.getDay() + 1);// 更新当前周显示function updateWeekDisplay() { const weekEnd = new Date(currentWeekStart); weekEnd.setDate(weekEnd.getDate() + 6); const options = { year: 'numeric', month: 'long', day: 'numeric' }; const startStr = currentWeekStart.toLocaleDateString('zh-CN', options); const endStr = weekEnd.toLocaleDateString('zh-CN', options); document.getElementById('current-week').textContent = `${startStr} - ${endStr}`; generateWeekHeader(); generateScheduleBody();}// 导航到上一周document.getElementById('prev-week').addEventListener('click', function() { currentWeekStart.setDate(currentWeekStart.getDate() - 7); updateWeekDisplay(); // 这里应该重新加载排班数据});// 导航到下一周document.getElementById('next-week').addEventListener('click', function() { currentWeekStart.setDate(currentWeekStart.getDate() + 7); updateWeekDisplay(); // 这里应该重新加载排班数据});// 初始化周显示updateWeekDisplay();// 页面加载时初始化动态内容document.addEventListener('DOMContentLoaded', function() { initDynamicContent();});// 模态框控制const shiftModal = document.getElementById('shift-modal');const employeeModal = document.getElementById('employee-modal');const closeButtons = document.getElementsByClassName('close');// 关闭模态框Array.from(closeButtons).forEach(button => { button.addEventListener('click', function() { shiftModal.style.display = 'none'; employeeModal.style.display = 'none'; });});// 点击模态框外部关闭window.addEventListener('click', function(event) { if (event.target == shiftModal) { shiftModal.style.display = 'none'; } if (event.target == employeeModal) { employeeModal.style.display = 'none'; }});// 班次类型变化时显示/隐藏自定义时间document.getElementById('shift-type').addEventListener('change', function() { const customTimeContainer = document.getElementById('custom-time-container'); if (this.value === 'custom') { customTimeContainer.style.display = 'block'; } else { customTimeContainer.style.display = 'none'; }});// 使用事件委托处理动态生成的元素document.addEventListener('click', function(e) { // 处理班次编辑 if (e.target.closest('.shift-slot')) { e.stopPropagation(); handleShiftEdit(e.target.closest('.shift-slot')); } // 处理添加班次 if (e.target.closest('.day-column') && (e.target.classList.contains('btn-outline') || e.target.closest('.empty-state'))) { const column = e.target.closest('.day-column'); document.getElementById('modal-title').textContent = '添加班次'; document.getElementById('shift-id').value = ''; document.getElementById('shift-form').reset(); document.getElementById('delete-shift').style.display = 'none'; document.getElementById('custom-time-container').style.display = 'none'; const dayOffset = parseInt(column.dataset.day) - 1; const date = getDateByDayOffset(dayOffset); document.getElementById('shift-date').value = formatDate(date); shiftModal.style.display = 'block'; }});// 根据星期几获取日期function getDateByDayOffset(dayOffset) { const date = new Date(currentWeekStart); date.setDate(date.getDate() + dayOffset); return date;}// 格式化日期为YYYY-MM-DDfunction formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`;}// 生成员工列表function generateEmployeeList() { const container = document.getElementById('employee-items-container'); container.innerHTML = ''; employees.forEach(employee => { const employeeItem = document.createElement('div'); employeeItem.className = 'employee-item'; employeeItem.dataset.id = employee.id; employeeItem.setAttribute('draggable', 'true'); employeeItem.innerHTML = ` <div class="employee-avatar">${employee.name.charAt(0)}</div> <div class="employee-info"> <div class="employee-name">${employee.name}</div> <div class="employee-position">${employee.position}</div> </div> `; container.appendChild(employeeItem); });}// 生成员工选择下拉框选项function generateEmployeeOptions() { const select = document.getElementById('shift-employee'); const firstOption = select.firstElementChild; select.innerHTML = ''; select.appendChild(firstOption); employees.forEach(employee => { const option = document.createElement('option'); option.value = employee.id; option.textContent = `${employee.name} - ${employee.position}`; select.appendChild(option); });}// 生成星期日期显示function generateWeekHeader() { const header = document.getElementById('schedule-header'); header.innerHTML = ''; const weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; const today = new Date(); weekDays.forEach((dayName, index) => { const date = getDateByDayOffset(index); const dateStr = `${date.getMonth() + 1}月 ${date.getDate()}日`; const dayElement = document.createElement('div'); if (date.toDateString() === today.toDateString()) { dayElement.className = 'today'; } dayElement.innerHTML = ` <div class="day-name">${dayName}</div> <div class="day-date">${dateStr}</div> `; header.appendChild(dayElement); });}// 生成排班表格function generateScheduleBody() { const body = document.getElementById('schedule-body'); body.innerHTML = ''; const today = new Date(); for (let day = 1; day <= 7; day++) { const dayColumn = document.createElement('div'); dayColumn.className = 'day-column'; dayColumn.dataset.day = day; const date = getDateByDayOffset(day - 1); if (date.toDateString() === today.toDateString()) { dayColumn.className = 'day-column today'; } const dayShifts = shifts.filter(shift => shift.day === day); if (dayShifts.length > 0) { dayShifts.forEach(shift => { const shiftType = shiftTypes.find(s => s.value === shift.type); const employee = employees.find(e => e.id === shift.employeeId); if (shiftType && employee) { const shiftSlot = document.createElement('div'); shiftSlot.className = `shift-slot shift-${shift.type}`; shiftSlot.dataset.shiftId = shift.id; shiftSlot.innerHTML = ` <div class="shift-header"> <span class="shift-type">${shiftType.name}</span> <span class="shift-time">${shiftType.time}</span> </div> <div class="shift-employee"> <div class="shift-avatar">${employee.name.charAt(0)}</div> <span>${employee.name}</span> </div> ${shift.notes ? `<div class="shift-notes">${shift.notes}</div>` : ''} `; dayColumn.appendChild(shiftSlot); } }); } if (dayShifts.length === 0) { const emptyState = document.createElement('div'); emptyState.className = 'empty-state'; const date = getDateByDayOffset(day - 1); const today = new Date(); const isToday = date.toDateString() === today.toDateString(); emptyState.innerHTML = ` <i class="fas ${isToday ? 'fa-calendar-plus' : 'fa-user-clock'}"></i> <p>${isToday ? '点击添加班次' : '暂无排班安排'}</p> <button class="btn btn-outline"><i class="fas fa-plus"></i>添加班次</button> `; dayColumn.appendChild(emptyState); } body.appendChild(dayColumn); }}// 初始化动态内容function initDynamicContent() { generateEmployeeList(); generateEmployeeOptions(); generateWeekHeader(); generateScheduleBody();}// 处理班次编辑function handleShiftEdit(shiftSlot) { document.getElementById('modal-title').textContent = '编辑班次'; document.getElementById('shift-id').value = shiftSlot.dataset.shiftId; // 设置员工 const employeeName = shiftSlot.querySelector('.shift-employee span').textContent; const employee = employees.find(emp => emp.name === employeeName); document.getElementById('shift-employee').value = employee ? employee.id : ''; // 设置班次类型 const shiftType = shiftSlot.querySelector('.shift-type').textContent; const selectedShift = shiftTypes.find(s => s.name === shiftType); document.getElementById('shift-type').value = selectedShift ? selectedShift.value : ''; // 设置备注 const notesEl = shiftSlot.querySelector('.shift-notes'); document.getElementById('shift-notes').value = notesEl ? notesEl.textContent : ''; document.getElementById('delete-shift').style.display = 'inline-block'; // 设置日期 const dayOffset = parseInt(shiftSlot.closest('.day-column').dataset.day) - 1; const date = getDateByDayOffset(dayOffset); document.getElementById('shift-date').value = formatDate(date); shiftModal.style.display = 'block';}// 提交班次表单document.getElementById('shift-form').addEventListener('submit', function(e) { e.preventDefault(); const shiftId = document.getElementById('shift-id').value; const employeeId = document.getElementById('shift-employee').value; const shiftType = document.getElementById('shift-type').value; const shiftDate = document.getElementById('shift-date').value; const shiftNotes = document.getElementById('shift-notes').value; if (!employeeId || !shiftType || !shiftDate) { alert('请填写完整的班次信息'); return; } const date = new Date(shiftDate); const weekStart = new Date(currentWeekStart); date.setHours(0, 0, 0, 0); weekStart.setHours(0, 0, 0, 0); const dayOffset = Math.floor((date - weekStart) / (1000 * 60 * 60 * 24)); const day = dayOffset + 1; if (shiftId) { const existingShift = shifts.find(s => s.id === shiftId); if (existingShift) { existingShift.employeeId = employeeId; existingShift.type = shiftType; existingShift.notes = shiftNotes; } } else { const newShift = { id: String(Date.now()), day: day, employeeId: employeeId, type: shiftType, notes: shiftNotes }; shifts.push(newShift); } generateScheduleBody(); document.getElementById('shift-form').reset(); document.getElementById('shift-id').value = ''; document.getElementById('delete-shift').style.display = 'none'; shiftModal.style.display = 'none'; alert('班次已保存');});// 删除班次document.getElementById('delete-shift').addEventListener('click', function() { if (confirm('确定要删除这个班次吗?')) { const shiftId = document.getElementById('shift-id').value; if (shiftId) { const shiftIndex = shifts.findIndex(s => s.id === shiftId); if (shiftIndex > -1) { shifts.splice(shiftIndex, 1); } } generateScheduleBody(); document.getElementById('shift-form').reset(); document.getElementById('shift-id').value = ''; document.getElementById('delete-shift').style.display = 'none'; shiftModal.style.display = 'none'; alert('班次已删除'); }});// 添加员工按钮document.getElementById('add-employee').addEventListener('click', function() { document.getElementById('employee-form').reset(); employeeModal.style.display = 'block';});// 提交员工表单document.getElementById('employee-form').addEventListener('submit', function(e) { e.preventDefault(); const name = document.getElementById('employee-name').value; const position = document.getElementById('employee-position').value; const phone = document.getElementById('employee-phone').value; const email = document.getElementById('employee-email').value; if (!name.trim()) { alert('请输入员工姓名'); return; } const newEmployee = { id: String(Date.now()), name: name, position: position || '未设置职位', phone: phone, email: email }; employees.push(newEmployee); generateEmployeeList(); generateEmployeeOptions(); document.getElementById('employee-form').reset(); employeeModal.style.display = 'none'; alert('员工已添加');});// 拖放功能 (简化版)let draggedEmployee = null;document.addEventListener('dragstart', function(e) { if (e.target.closest('.employee-item')) { draggedEmployee = e.target.closest('.employee-item'); setTimeout(() => { draggedEmployee.style.opacity = '0.4'; }, 0); }});document.addEventListener('dragend', function(e) { if (e.target.closest('.employee-item')) { e.target.closest('.employee-item').style.opacity = '1'; }});document.addEventListener('dragover', function(e) { if (e.target.closest('.day-column')) { e.preventDefault(); e.target.closest('.day-column').style.backgroundColor = 'rgba(67, 97, 238, 0.1)'; }});document.addEventListener('dragleave', function(e) { if (e.target.closest('.day-column')) { e.target.closest('.day-column').style.backgroundColor = ''; }});document.addEventListener('drop', function(e) { if (e.target.closest('.day-column')) { e.preventDefault(); const column = e.target.closest('.day-column'); column.style.backgroundColor = ''; if (draggedEmployee) { const shiftSlot = document.createElement('div'); shiftSlot.className = 'shift-slot shift-morning'; shiftSlot.dataset.shiftId = Date.now(); const employeeName = draggedEmployee.querySelector('.employee-name').textContent; const employeeAvatar = draggedEmployee.querySelector('.employee-avatar').textContent; shiftSlot.innerHTML = ` <div class="shift-header"> <span class="shift-type">早班</span> <span class="shift-time">8:00 - 16:00</span> </div> <div class="shift-employee"> <div class="shift-avatar">${employeeAvatar}</div> <span>${employeeName}</span> </div> `; const emptyState = column.querySelector('.empty-state'); if (emptyState) { column.removeChild(emptyState); } column.appendChild(shiftSlot); draggedEmployee = null; } }});
源码获取
私信回复排班系统获取完整源码
夜雨聆风