基于jQuery/Bootstrap的无复选框预约调度数据保存方案咨询
Hey Wesley! Let's tackle this appointment scheduling feature step by step. Your current approach with multiple hidden inputs is getting unwieldy—let's simplify it with a single JSON-backed hidden field, cleaner click logic, and a straightforward database setup.
1. Frontend Refactor: Simplify Selection & State Management
Instead of having separate hidden inputs for each possible slot, we'll use a single hidden field to store the entire schedule as JSON. This makes it easier to track selections, handle cancellations, and avoid messy input naming.
Updated HTML Structure
We'll add data-* attributes to each slot to make identifying days and slot types easier, and use a single hidden input for the schedule:
<form action="save-schedule.php" method="post"> <!-- Single hidden field to store schedule state as JSON --> <input type="hidden" name="schedule" id="schedule" value='{}'> <div class="row" id="days"> <div class="col-lg-1"></div> <div class="col-lg-2 day-column" data-day="monday"> <h3>MONDAY</h3> <div id="finaldates"></div> <!-- Default slots with data attributes --> <p class="slot default-slot" data-slot="05:00a - 8:00a">05:00a - 8:00a</p> <p class="slot default-slot" data-slot="05:30a - 8:30a">05:30a - 8:30a</p> <p class="slot default-slot" data-slot="06:00a - 9:00a">06:00a - 9:00a</p> <p class="slot default-slot" data-slot="07:00a - 10:00a">07:00a - 10:00a</p> <p class="slot default-slot" data-slot="10:00a - 1:00p">10:00a - 1:00p</p> <p class="slot default-slot" data-slot="10:00a - 2:00p">10:00a - 2:00p</p> <div class="buffer"></div> <img src="pics/PM-icon.png" alt="PM Slots"> <p class="slot default-slot" data-slot="03:00p - 6:00p">03:00p - 6:00p</p> <p class="slot default-slot" data-slot="03:30p - 6:30p">03:30p - 6:30p</p> <p class="slot default-slot" data-slot="04:00p - 7:00p">04:00p - 7:00p</p> <!-- Special options --> <p class="slot custom-option" data-type="custom">Choose Your Own</p> <p class="slot no-appt-option" data-type="no-appt">NO MONDAYS</p> </div> <!-- Repeat similar structure for Tuesday-Friday --> </div> <button type="submit">Save Schedule</button> </form> <!-- Add some CSS for selected state --> <style> .slot { cursor: pointer; padding: 5px; margin: 3px 0; border-radius: 4px; } .slot.selected { background-color: #007bff; color: white; } .day-column .no-appt-option.selected ~ .slot { opacity: 0.5; /* Dim other slots if "no appointments" is selected */ } </style>
jQuery Logic for Selection & Cancellation
This code will handle clicking slots, tracking the schedule state, updating the hidden field, and managing the selected UI state:
$(document).ready(function() { // Initialize schedule from hidden field (or empty object) let schedule = JSON.parse($('#schedule').val() || '{}'); // Helper to update the hidden field and UI function updateSchedule() { $('#schedule').val(JSON.stringify(schedule)); // Refresh UI state $('.slot').removeClass('selected'); Object.keys(schedule).forEach(day => { const dayData = schedule[day]; if (dayData.noAppointment) { $(`.day-column[data-day="${day}"] .no-appt-option`).addClass('selected'); } else if (dayData.slots) { dayData.slots.forEach(slot => { $(`.day-column[data-day="${day}"] .slot[data-slot="${slot}"]`).addClass('selected'); }); if (dayData.customSlot) { // Display custom slot in the finaldates container $(`.day-column[data-day="${day}"] #finaldates`).text(`Custom: ${dayData.customSlot}`); } else { $(`.day-column[data-day="${day}"] #finaldates`).empty(); } } }); } // Load initial state updateSchedule(); // Handle slot clicks $('.slot').click(function() { const $slot = $(this); const day = $slot.closest('.day-column').data('day'); const slotType = $slot.data('type'); const slotValue = $slot.data('slot'); // Initialize day entry if it doesn't exist if (!schedule[day]) { schedule[day] = { slots: [], customSlot: null, noAppointment: false }; } const daySchedule = schedule[day]; // Case 1: "NO MONDAYS" (no appointments) selected if (slotType === 'no-appt') { daySchedule.noAppointment = !daySchedule.noAppointment; // Reset other options if enabling no appointments if (daySchedule.noAppointment) { daySchedule.slots = []; daySchedule.customSlot = null; } } // Case 2: Custom slot selection else if (slotType === 'custom') { const customSlot = prompt("Enter your custom time slot (e.g., 09:00a - 12:00p):"); if (customSlot) { daySchedule.customSlot = customSlot; daySchedule.noAppointment = false; // Ensure we don't exceed 2 slots total (custom + 1 default) if (daySchedule.slots.length > 1) { daySchedule.slots.pop(); } } } // Case 3: Default slot selection else { daySchedule.noAppointment = false; daySchedule.customSlot = null; // Clear custom if selecting default const slotIndex = daySchedule.slots.indexOf(slotValue); if (slotIndex !== -1) { // Remove slot if already selected (cancellation) daySchedule.slots.splice(slotIndex, 1); } else { // Add slot if under 2 selections if (daySchedule.slots.length < 2) { daySchedule.slots.push(slotValue); } else { alert(`You can only select 2 slots per day for ${day.charAt(0).toUpperCase() + day.slice(1)}!`); return; } } } // Update the schedule state updateSchedule(); }); });
2. Backend: PHP Handling & Database Storage
First, let's define a clean database table structure to store the schedule. We'll use a row per day/slot combination for flexibility:
Database Table Schema
CREATE TABLE appointment_schedules ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, -- Link to your users table day_of_week ENUM('monday', 'tuesday', 'wednesday', 'thursday', 'friday') NOT NULL, slot VARCHAR(50) DEFAULT NULL, -- For default slots custom_slot VARCHAR(50) DEFAULT NULL, -- For custom slots is_no_appointment BOOLEAN DEFAULT FALSE, -- For "no appointments" option start_date DATE NOT NULL, -- For 3-month coverage (adjust to your date range) end_date DATE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) -- Adjust to your users table name );
PHP Code to Save the Schedule
Create a save-schedule.php file to handle form submission:
<?php // Assume you have a database connection established (PDO or mysqli) // Example PDO connection: $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Get the logged-in user ID (adjust this to your auth system) $userId = 1; // Replace with actual user ID // Define 3-month date range (adjust start/end as needed) $startDate = date('Y-m-d'); $endDate = date('Y-m-d', strtotime('+3 months')); // Get the schedule JSON from the form $scheduleJson = $_POST['schedule'] ?? '{}'; $schedule = json_decode($scheduleJson, true); // First, delete existing schedule for the user in this date range $stmt = $pdo->prepare("DELETE FROM appointment_schedules WHERE user_id = ? AND start_date = ? AND end_date = ?"); $stmt->execute([$userId, $startDate, $endDate]); // Insert new schedule entries foreach ($schedule as $day => $dayData) { if ($dayData['noAppointment']) { // Insert "no appointments" entry $stmt = $pdo->prepare("INSERT INTO appointment_schedules (user_id, day_of_week, is_no_appointment, start_date, end_date) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$userId, $day, true, $startDate, $endDate]); } else { // Insert default slots foreach ($dayData['slots'] as $slot) { $stmt = $pdo->prepare("INSERT INTO appointment_schedules (user_id, day_of_week, slot, start_date, end_date) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$userId, $day, $slot, $startDate, $endDate]); } // Insert custom slot if exists if (!empty($dayData['customSlot'])) { $stmt = $pdo->prepare("INSERT INTO appointment_schedules (user_id, day_of_week, custom_slot, start_date, end_date) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$userId, $day, $dayData['customSlot'], $startDate, $endDate]); } } } // Redirect back or show success message header("Location: your-schedule-page.php?success=1"); exit; ?>
Key Improvements Over Your Current Setup
- Simpler State Management: Using JSON to store the entire schedule eliminates the need for dozens of hidden inputs.
- Built-in Cancellation: Clicking an already selected slot removes it from the schedule.
- Clear Slot Limits: Enforces the 2-slot per day rule with a user-friendly alert.
- Flexible Database Storage: The table structure supports default slots, custom slots, and "no appointment" options, plus the 3-month coverage you mentioned.
- Maintainable Code: Separating concerns (frontend state, backend storage) makes it easier to extend later (like adding date range pickers for the 3-month window).
内容的提问来源于stack exchange,提问作者Wesley

