diff --git a/src/assets/Styles.css b/src/assets/Styles.css index 1ed2f7e..d8a6719 100644 --- a/src/assets/Styles.css +++ b/src/assets/Styles.css @@ -90,6 +90,7 @@ header { max-width: 100%; width: 538px; margin-bottom: 5rem; + position: relative; } .calculator * { transition: background-color 1s, color 0.1s ease-in-out; @@ -100,27 +101,40 @@ header { font-size: 32px; font-weight: 700; cursor: pointer; + font-family: var(--main-font); } .screen { - font-size: 3.3rem; - padding: 2.5rem 1.9rem; + padding: 1.5rem 1.9rem; text-align: right; - height: 2.9rem; + min-height: 4.5rem; letter-spacing: 1px; border-radius: 5px; margin-top: 0.9rem; - text-align: right; display: flex; - flex-direction: row; + flex-direction: column; justify-content: flex-end; + align-items: flex-end; + gap: 0.3rem; +} + +.screen-expression { + font-size: 1.2rem; + opacity: 0.7; + word-break: break-all; + font-family: var(--main-font); +} + +.screen-result { + font-size: 3.3rem; + font-family: var(--main-font); } .keypad { display: grid; row-gap: 1.9rem; column-gap: 1.3rem; - grid-template: repeat(5, 1fr) / repeat(4, 1fr); + grid-template: repeat(6, 1fr) / repeat(4, 1fr); grid-auto-flow: row; padding: 2rem; border-radius: 5px; @@ -137,14 +151,116 @@ header { } #del, #reset-key, -#equals-key { +#equals-key, +.history-toggle { font-size: 1.5rem; } #reset-key { grid-column: 1 / span 2; } +.history-toggle { + grid-column: 3 / span 1; +} #equals-key { - grid-column: 3 / span 2; + grid-column: 4 / span 1; +} + +/* HISTORY PANEL */ +.history-panel { + margin-top: 1.6rem; + padding: 1.5rem; + max-height: 400px; + overflow-y: auto; + animation: slideDown 0.3s ease-out; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.history-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.history-header h3 { + font-size: 1.2rem; + font-family: var(--main-font); +} + +.clear-history-btn { + font-size: 0.85rem !important; + padding: 0.4rem 1rem; + border-radius: 5px; + font-family: var(--main-font); +} + +.history-list { + display: flex; + flex-direction: column; + gap: 0.8rem; +} + +.history-item { + padding: 1rem 1.2rem; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.history-item:hover { + transform: translateX(5px); +} + +.history-expression { + font-size: 0.95rem; + word-break: break-all; + font-family: var(--main-font); +} + +.history-result { + font-size: 1.4rem; + font-weight: 700; + font-family: var(--main-font); +} + +.empty-history { + text-align: center; + padding: 2rem; + font-size: 1rem; + font-family: var(--main-font); +} + +/* Scrollbar styling for history panel */ +.history-panel::-webkit-scrollbar { + width: 6px; +} + +.history-panel::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.1); + border-radius: 3px; +} + +.history-panel::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.2); + border-radius: 3px; +} + +.history-panel::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.3); } @media screen and (max-width: 600px) { @@ -156,5 +272,7 @@ header { @media screen and (max-width: 440px) { .keypad { padding: 1rem; + row-gap: 1rem; + column-gap: 0.8rem; } } diff --git a/src/components/calculator/Calculator.jsx b/src/components/calculator/Calculator.jsx index cd8213d..370dae5 100644 --- a/src/components/calculator/Calculator.jsx +++ b/src/components/calculator/Calculator.jsx @@ -1,17 +1,49 @@ -import { useState } from "react"; +import { useState, useEffect, useCallback } from "react"; import React from "react"; -import { useEffect } from "react"; import Screen from "@/components/calculator/Screen"; import Keypad from "@/components/calculator/Keypad"; +import History from "@/components/calculator/History"; export default function Calculator() { const [calc, setCalc] = useState(""); const [calculated, setCalculated] = useState(false); + const [history, setHistory] = useState([]); + const [showHistory, setShowHistory] = useState(false); + const [currentExpression, setCurrentExpression] = useState(""); const operations = ["/", "*", "+", "-", "."]; const numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]; const ref = React.createRef(); + // 从 localStorage 加载历史记录 + useEffect(() => { + const savedHistory = localStorage.getItem("calculator-history"); + if (savedHistory) { + try { + setHistory(JSON.parse(savedHistory)); + } catch (e) { + console.error("Failed to parse history:", e); + } + } + }, []); + + // 保存历史记录到 localStorage + useEffect(() => { + localStorage.setItem("calculator-history", JSON.stringify(history)); + }, [history]); + + const addToHistory = useCallback((expression, result) => { + const newHistoryItem = { + expression: expression, + result: result, + timestamp: new Date().toISOString(), + }; + setHistory((prevHistory) => [newHistoryItem, ...prevHistory].slice(0, 50)); // 最多保留50条记录 + }, []); + const updateCalc = (value) => { + // 当开始新的输入时,清除之前的表达式显示 + setCurrentExpression(""); + const displayValue = ref.current; //************* BEFORE CALCULATION *************// if (value === "0" && calc === "0") { @@ -21,9 +53,7 @@ export default function Calculator() { (operations.includes(value) && operations.includes(calc.slice(-1))) ) { return; - } else if (!operations.includes(value)) { - setCalc((+calc + +value).toString()); - + } else if (!operations.includes(value) && !calculated) { if (calc.length > 7) { displayValue.style.fontSize = "2.5rem"; } @@ -31,10 +61,6 @@ export default function Calculator() { displayValue.style.width = "fit-content"; displayValue.style.overflow = "hidden"; return setCalc(calc.toString()); - } else if (calc.length === 11 && operations.includes(value)) { - console.log("hi"); - } else { - console.error(); } } @@ -50,29 +76,45 @@ export default function Calculator() { } else if (calculated === true && value === "0") { setCalculated(false); return setCalc(""); - } else { - console.error(); } setCalc(calc + value); }; const calculate = () => { - if (eval(calc) === undefined) { + if (!calc || calc === "") { return; } - setCalculated(true); - setCalc(eval(calc).toString()); + try { + const result = eval(calc).toString(); + setCalculated(true); + addToHistory(calc, result); + // 计算完成后,设置表达式以便显示 + setCurrentExpression(calc); + setCalc(result); + } catch (e) { + console.error("Calculation error:", e); + } }; + const clear = () => { setCalculated(false); setCalc(""); + setCurrentExpression(""); // 清除表达式显示 + const displayValue = ref.current; + if (displayValue) { + displayValue.style.fontSize = "3.3rem"; + displayValue.style.width = "auto"; + displayValue.style.overflow = "visible"; + } }; + const del = () => { if (calc === "") { return; } + setCurrentExpression(""); // 删除时清除表达式显示 const displayValue = ref.current; - if (calc.length < 7) { + if (calc.length < 8) { displayValue.style.fontSize = "3.3rem"; } @@ -80,29 +122,58 @@ export default function Calculator() { setCalc(value); }; + // 处理历史记录点击 - 同时显示表达式和结果 + const handleHistoryClick = (item) => { + setCurrentExpression(item.expression); + setCalc(item.result); + setCalculated(true); + }; + + // 清除历史记录 + const handleClearHistory = () => { + setHistory([]); + localStorage.removeItem("calculator-history"); + }; + + // 切换历史记录显示 + const toggleHistory = () => { + setShowHistory(!showHistory); + }; + const onKeyDown = (e) => { if (operations.includes(e.key) || numbers.includes(e.key)) { updateCalc(e.key); } else if (e.key === "Enter" || e.key === "=") { calculate(); } else if (e.key === "Backspace") del(); + else if (e.key === "Escape") clear(); }; + useEffect(() => { window.addEventListener("keydown", onKeyDown); return () => { window.removeEventListener("keydown", onKeyDown); }; }); + return (
- + +
diff --git a/src/components/calculator/History.jsx b/src/components/calculator/History.jsx new file mode 100644 index 0000000..a59c008 --- /dev/null +++ b/src/components/calculator/History.jsx @@ -0,0 +1,36 @@ +import React from "react"; + +const History = ({ history, onHistoryClick, onClearHistory, isVisible }) => { + if (!isVisible) return null; + + return ( +
+
+

计算历史

+ {history.length > 0 && ( + + )} +
+
+ {history.length === 0 ? ( +

暂无计算记录

+ ) : ( + history.map((item, index) => ( +
onHistoryClick(item)} + > +
{item.expression}
+
= {item.result}
+
+ )) + )} +
+
+ ); +}; + +export default History; diff --git a/src/components/calculator/Keypad.jsx b/src/components/calculator/Keypad.jsx index 778d53e..8edaa89 100644 --- a/src/components/calculator/Keypad.jsx +++ b/src/components/calculator/Keypad.jsx @@ -80,6 +80,13 @@ const Keypad = (props) => { > RESET +