import React, { useState, useMemo } from "react"; import { Calculator, Plus, Minus, Save, Trash, ChevronDown, ChevronUp } from "lucide-react"; import * as XLSX from "xlsx"; const Field = ({ label, suffix, value, onChange, placeholder = "", step = 0.1, type = "number" }) => (
onChange(type === 'number' ? (parseFloat(e.target.value) || 0) : e.target.value)} style={{ padding: '6px 8px', width: '100%', boxSizing: 'border-box' }} /> {suffix && {suffix}}
); export default function MultiRoomConstructionCalculator() { const [roomName, setRoomName] = useState(""); const [length, setLength] = useState(""); const [width, setWidth] = useState(""); const [height, setHeight] = useState(""); const [windows, setWindows] = useState([]); const [doors, setDoors] = useState([]); const [rooms, setRooms] = useState([]); const [expandedRooms, setExpandedRooms] = useState([]); const addWindow = () => setWindows([...windows, { width: 1.2, height: 1.5 }]); const removeWindow = (i) => setWindows(windows.filter((_, idx) => idx !== i)); const updateWindow = (i, key, value) => { const updated = [...windows]; updated[i][key] = value; setWindows(updated); }; const addDoor = () => setDoors([...doors, { width: 0.9, height: 2.0 }]); const removeDoor = (i) => setDoors(doors.filter((_, idx) => idx !== i)); const updateDoor = (i, key, value) => { const updated = [...doors]; updated[i][key] = value; setDoors(updated); }; const results = useMemo(() => { if (!length || !width || !height) return null; const perimeter = 2 * (parseFloat(length) + parseFloat(width)); const wallArea = perimeter * parseFloat(height); const floorArea = parseFloat(length) * parseFloat(width); const windowsArea = windows.reduce((sum, o) => sum + (parseFloat(o.width) || 0) * (parseFloat(o.height) || 0), 0); const doorsArea = doors.reduce((sum, o) => sum + (parseFloat(o.width) || 0) * (parseFloat(o.height) || 0), 0); const netArea = wallArea - (windowsArea + doorsArea); return { perimeter, wallArea, floorArea, windowsArea, doorsArea, netArea }; }, [length, width, height, windows, doors]); const saveRoom = () => { if (!results || !roomName) return; setRooms([ ...rooms, { name: roomName, perimeter: results.perimeter, wallArea: results.wallArea, floorArea: results.floorArea, windowsArea: results.windowsArea, doorsArea: results.doorsArea, netArea: results.netArea, }, ]); setRoomName(""); setLength(""); setWidth(""); setHeight(""); setWindows([]); setDoors([]); }; const deleteRoom = (i) => setRooms(rooms.filter((_, idx) => idx !== i)); const toggleExpand = (i) => { if (expandedRooms.includes(i)) { setExpandedRooms(expandedRooms.filter(idx => idx !== i)); } else { setExpandedRooms([...expandedRooms, i]); } }; const exportExcel = () => { if (!results && rooms.length === 0) return; const wsData = [ ["Помещение", "Периметр (м)", "Площадь стен без вычета (м²)", "Площадь пола (м²)", "Площадь окон (м²)", "Площадь дверей (м²)", "Площадь стен с вычетом (м²)"], ...rooms.map(r => [r.name, r.perimeter, r.wallArea, r.floorArea, r.windowsArea, r.doorsArea, r.netArea]), ]; if (results && roomName) { wsData.push([roomName, results.perimeter, results.wallArea, results.floorArea, results.windowsArea, results.doorsArea, results.netArea]); } const totalPerimeter = rooms.reduce((sum, r) => sum + r.perimeter, results ? results.perimeter : 0); const totalWall = rooms.reduce((sum, r) => sum + r.wallArea, results ? results.wallArea : 0); const totalFloor = rooms.reduce((sum, r) => sum + r.floorArea, results ? results.floorArea : 0); const totalWindows = rooms.reduce((sum, r) => sum + r.windowsArea, results ? results.windowsArea : 0); const totalDoors = rooms.reduce((sum, r) => sum + r.doorsArea, results ? results.doorsArea : 0); const totalNet = rooms.reduce((sum, r) => sum + r.netArea, results ? results.netArea : 0); wsData.push(["ИТОГО", totalPerimeter, totalWall, totalFloor, totalWindows, totalDoors, totalNet]); const ws = XLSX.utils.aoa_to_sheet(wsData); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Rooms Report"); const today = new Date().toISOString().split("T")[0]; XLSX.writeFile(wb, `report_${today}.xlsx`, { compression: true }); }; return (

Строительный Калькулятор

Добавляйте несколько помещений, учитывайте окна и двери и выгружайте общий отчёт в Excel.

Окна

{windows.map((o, i) => (
updateWindow(i, 'width', v)} /> updateWindow(i, 'height', v)} />
))}

Двери

{doors.map((o, i) => (
updateDoor(i, 'width', v)} /> updateDoor(i, 'height', v)} />
))}
{rooms.map((r, i) => (
toggleExpand(i)} style={{ cursor: 'pointer', fontWeight: 'bold' }}>{r.name}
{expandedRooms.includes(i) && (
Периметр: {r.perimeter.toFixed(2)} м
Площадь стен без вычета: {r.wallArea.toFixed(2)} м²
Площадь пола: {r.floorArea.toFixed(2)} м²
Площадь окон: {r.windowsArea.toFixed(2)} м²
Площадь дверей: {r.doorsArea.toFixed(2)} м²
Площадь стен с вычетом: {r.netArea.toFixed(2)} м²
)}
))}
); }
Made on
Tilda