React周历拖拽功能异常:位置偏移+数据不持久
拖拽日历盒子的问题修复方案
问题说明
- 拖拽盒子到指定时间槽时,会落到该日期的首个空位置,而非目标位置
- 页面刷新后,所有拖拽调整过的盒子都会恢复初始状态
修复方案
问题1:拖拽位置错误修复
当前代码在拖拽完成后,是删除原订单再将修改后的订单push到数组末尾,导致渲染时会被填充到第一个空槽位。需要给每个订单绑定明确的时间槽索引,直接更新目标订单的日期和槽位信息。
问题2:刷新数据丢失修复
使用localStorage持久化订单数据,初始化时优先读取本地存储的内容,更新数据时同步保存到本地。
修改后的完整代码
import React, { useState, useRef, useEffect } from "react"; import { startOfWeek, endOfWeek, format, addDays, subDays } from "date-fns"; import "../assets/css/RunSheetPDF.css"; const DraggableBox = ({ day, order, setOrderData, setChangeData }) => { const [showTooltip, setShowTooltip] = useState(false); const boxRef = useRef(null); const handleDragStart = (e) => { setShowTooltip(false); setChangeData(order); boxRef.current.classList.add("dragging"); }; const handleDragEnd = () => { boxRef.current.classList.remove("dragging"); }; const handleDragOver = (e) => { e.preventDefault(); }; return ( <div className="drag-box" ref={boxRef} draggable onDragStart={handleDragStart} onDragEnd={handleDragEnd} onDragOver={handleDragOver} onMouseEnter={() => setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)} style={{ width: "70px", height: "80px", textAlign: "center", backgroundColor: "#F5F5F5", color: "#333333", marginTop: "5px", position: "relative", cursor: "move", }} data-day={day} > <div style={{ margin: 0, lineHeight: "40px" }}> <p style={{ margin: 0 }}>{order.Id}</p> <p style={{ margin: 0 }}>{order.DelCity}</p> </div> </div> ); }; const RunSheetPreview = ({ initialOrderData, selectedDate: propSelectedDate, }) => { const [changeData, setChangeData] = useState(null); const [selectedDate, setSelectedDate] = useState(propSelectedDate || new Date()); // 从localStorage读取持久化数据 const [orderData, setOrderData] = useState(() => { const savedData = localStorage.getItem("runSheetOrders"); return savedData ? JSON.parse(savedData) : initialOrderData; }); // 数据更新时同步保存到localStorage useEffect(() => { localStorage.setItem("runSheetOrders", JSON.stringify(orderData)); }, [orderData]); const formatDate = (date) => { const dateVal = new Date(date); const year = dateVal.getFullYear(); const month = String(dateVal.getMonth() + 1).padStart(2, "0"); const day = String(dateVal.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; }; const handleDragOver = (e) => { e.preventDefault(); }; const handleDrop = (e, targetTimeSlot, targetDay, targetYear, targetMonth) => { e.preventDefault(); e.stopPropagation(); if (!changeData) return; // 创建新的日期对象,设置目标日期 const newDate = new Date(changeData.OrderDate); newDate.setFullYear(targetYear); newDate.setMonth(targetMonth - 1); // 月份是0-based newDate.setDate(targetDay); // 更新订单的日期和时间槽索引 const updatedOrders = orderData.order.map(order => { if (order.Id === changeData.Id) { return { ...order, OrderDate: newDate.toISOString(), RequiredDateAndTime: newDate.toISOString(), timeSlotIndex: targetTimeSlot }; } return order; }); setOrderData({ ...orderData, order: updatedOrders }); setChangeData(null); }; const generateCalendar = () => { const startDate = startOfWeek(selectedDate, { weekStartsOn: 0 }); const endDate = endOfWeek(selectedDate, { weekStartsOn: 0 }); const rows = []; let day = startDate; while (day <= endDate) { const formattedDate = formatDate(day); const dateObject = new Date(formattedDate); const year = dateObject.getFullYear(); const month = dateObject.getMonth() + 1; const dayName = format(day, "EEEE"); const dayOfMonth = format(day, "d"); // 按时间槽索引排序当天的订单 const ordersForDay = orderData.order .filter(order => { const orderDate = new Date(order.RequiredDateAndTime).getDate(); return orderDate === parseInt(dayOfMonth); }) .sort((a, b) => (a.timeSlotIndex || 0) - (b.timeSlotIndex || 0)); const row = ( <tr key={day}> <td className="calendar-cell"> <div className="day-name">{dayName}</div> <div className="day-number">{dayOfMonth}</div> </td> {[...Array(10)].map((_, slotIndex) => { const matchedOrder = ordersForDay.find(order => order.timeSlotIndex === slotIndex); return ( <td key={slotIndex} className={`grid-cell`}> {matchedOrder ? ( <DraggableBox day={parseInt(dayOfMonth)} order={matchedOrder} setOrderData={setOrderData} setChangeData={setChangeData} /> ) : ( <p style={{ height: "90px", display: "flex", alignItems: "center", justifyContent: "center", }} onDragOver={handleDragOver} onDrop={(e) => handleDrop(e, slotIndex, parseInt(dayOfMonth), year, month)} data-year={year} data-month={month} ></p> )} </td> ); })} </tr> ); rows.push(row); day = addDays(day, 1); } return rows; }; const handleNextWeek = () => { setSelectedDate(addDays(selectedDate, 7)); }; const handlePreviousWeek = () => { setSelectedDate(subDays(selectedDate, 7)); }; return ( <div style={{ width: "100%", display: "flex", flexDirection: "column", alignItems: "center", marginTop: "20px" }}> <div style={{ display: "flex", alignItems: "center", justifyContent: "center", marginBottom: "0px" }}> <h2 style={{ color: "#333333", margin: 0 }}>{format(startOfWeek(selectedDate), "MMMM d")} - {format(endOfWeek(selectedDate), "MMMM d, yyyy")}</h2> </div> <div className="calendar-container"> <table className="calendar-table"> <thead> <tr> <th>{/* Empty header cell for labels */}</th> {[...Array(10)].map((_, index) => ( <th key={index}>t{index + 1}</th> ))} </tr> </thead> <tbody> {generateCalendar()} </tbody> </table> </div> <div style={{ marginTop: "10px" }}> <button onClick={handlePreviousWeek}>上一周</button> <button onClick={handleNextWeek} style={{ marginLeft: "10px" }}>下一周</button> </div> </div> ); }; export default RunSheetPreview;
内容的提问来源于stack exchange,提问作者user022yufjb
相关产品推荐
相关产品推荐

