# 科学计算器功能扩展实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 将桌面端计算器从基础四则运算扩展为完整科学计算器,支持三角/反三角/对数/幂/根/阶乘/记忆/括号等功能 **Architecture:** 在 CalculatorDesktop 中新增模式切换标签栏(基础/科学),科学模式下显示函数区。状态管理扩展 memory/angleMode/invMode/parenOpen/mode。保持 4 列布局,宽度不变。 **Tech Stack:** React, TypeScript, CSS, Ant Design (message) --- ## 文件结构 - **修改**: `src/pages/Calculator/Calculator.tsx` — 扩展 CalculatorDesktop 组件 - **修改**: `src/pages/Calculator/Calculator.css` — 新增科学函数按键样式、模式切换栏 - **不修改**: `src/pages/Calculator/CalculatorMobile.tsx` — 移动端保持不变 --- ## Task 1: 新增模式切换标签栏 **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - Modify: `src/pages/Calculator/Calculator.css` - [ ] **Step 1: 在 Calculator.tsx 中新增 mode 状态和切换逻辑** 在 `CalculatorState` 中添加 `mode: 'basic' | 'scientific'`,初始值为 `'basic'`。 新增函数: ```typescript const toggleMode = () => { setState((prev) => ({ ...prev, mode: prev.mode === 'basic' ? 'scientific' : 'basic', })); }; ``` - [ ] **Step 2: 在 display 区域上方渲染模式切换标签栏** 在 `calc-display` 之前添加: ```tsx
{state.mode === 'scientific' && ( {state.angleMode} )} {state.memory !== 0 && M}
``` - [ ] **Step 3: 在 Calculator.css 中添加模式切换栏样式** ```css .calc-mode-bar { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; background: #151928; border-bottom: 1px solid rgba(51, 65, 85, 0.5); } .calc-mode-tabs { display: flex; gap: 4px; } .calc-mode-tab { padding: 6px 16px; font-size: 12px; font-weight: 600; border: none; background: transparent; color: #64748b; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; } .calc-mode-tab.active { background: #6366f1; color: #fff; } .calc-mode-tab:hover:not(.active) { color: #94a3b8; background: rgba(99, 102, 241, 0.1); } .calc-mode-indicator { display: flex; gap: 6px; } .calc-angle-badge, .calc-memory-badge { font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 4px; background: rgba(99, 102, 241, 0.2); color: #a5b4fc; } .calc-memory-badge { background: rgba(34, 197, 94, 0.2); color: #86efac; } ``` - [ ] **Step 4: 添加国际化文本** 在 `zh.json` 和 `en.json` 的 `calculator` 节中添加: ```json "mode": { "basic": "基础", "scientific": "科学" } ``` - [ ] **Step 5: 提交** ```bash git add src/pages/Calculator/Calculator.tsx src/pages/Calculator/Calculator.css src/locales/zh.json src/locales/en.json git commit -m "feat(calculator): add mode toggle bar (basic/scientific)" ``` --- ## Task 2: 扩展状态管理 **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - [ ] **Step 1: 扩展 CalculatorState 接口** ```typescript interface CalculatorState { current: string; previous: string; operator: Operator | null; overwrite: boolean; history: string; memory: number; // 新增:记忆存储值 angleMode: 'DEG' | 'RAD'; // 新增:角度/弧度模式 invMode: boolean; // 新增:反函数模式(2nd键) parenOpen: boolean; // 新增:是否有未闭合的括号 mode: 'basic' | 'scientific'; // 新增:当前模式 } type Operator = '+' | '-' | '*' | '/' | '^' | 'root'; ``` - [ ] **Step 2: 更新初始状态** ```typescript const [state, setState] = useState({ current: "0", previous: "", operator: null, overwrite: false, history: "", memory: 0, angleMode: 'DEG', invMode: false, parenOpen: false, mode: 'basic', }); ``` - [ ] **Step 3: 新增记忆操作函数** ```typescript const memoryClear = () => { setState((prev) => ({ ...prev, memory: 0 })); }; const memoryRecall = () => { setState((prev) => ({ ...prev, current: String(prev.memory), overwrite: true, })); }; const memoryAdd = () => { setState((prev) => ({ ...prev, memory: prev.memory + parseFloat(prev.current), })); }; const memorySubtract = () => { setState((prev) => ({ ...prev, memory: prev.memory - parseFloat(prev.current), })); }; ``` - [ ] **Step 4: 新增角度模式切换函数** ```typescript const toggleAngleMode = () => { setState((prev) => ({ ...prev, angleMode: prev.angleMode === 'DEG' ? 'RAD' : 'DEG', })); }; ``` - [ ] **Step 5: 新增反函数模式切换函数** ```typescript const toggleInvMode = () => { setState((prev) => ({ ...prev, invMode: !prev.invMode })); }; ``` - [ ] **Step 6: 提交** ```bash git add src/pages/Calculator/Calculator.tsx git commit -m "feat(calculator): extend state management with memory, angle mode, inv mode" ``` --- ## Task 3: 新增科学函数区(科学模式专用) **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - Modify: `src/pages/Calculator/Calculator.css` - [ ] **Step 1: 在 buttons 数组前添加科学函数按钮定义** ```typescript const sciButtons = [ { label: state.invMode ? "sin⁻¹" : "sin", type: "function", onClick: sin }, { label: state.invMode ? "cos⁻¹" : "cos", type: "function", onClick: cos }, { label: state.invMode ? "tan⁻¹" : "tan", type: "function", onClick: tan }, { label: state.invMode ? "x³" : "x²", type: "function", onClick: state.invMode ? cube : square }, { label: "log", type: "function", onClick: log10 }, { label: state.invMode ? "eˣ" : "ln", type: "function", onClick: state.invMode ? exp : ln }, { label: state.invMode ? "³√" : "√", type: "function", onClick: state.invMode ? cbrt : sqrt }, { label: "π", type: "function", onClick: insertPi }, { label: "e", type: "function", onClick: insertE }, { label: state.invMode ? "x√y" : "xʸ", type: "operator", onClick: state.invMode ? nthRoot : power }, { label: "n!", type: "function", onClick: factorial }, { label: "1/x", type: "function", onClick: reciprocal }, { label: "DEG/RAD", type: "function", onClick: toggleAngleMode }, { label: "2nd", type: "function", onClick: toggleInvMode }, { label: "MC", type: "memory", onClick: memoryClear }, { label: "MR", type: "memory", onClick: memoryRecall }, { label: "M+", type: "memory", onClick: memoryAdd }, { label: "M−", type: "memory", onClick: memorySubtract }, { label: "(", type: "function", onClick: openParen }, { label: ")", type: "function", onClick: closeParen }, ]; ``` - [ ] **Step 2: 在渲染 keypad 时,科学模式下显示 sciButtons** ```tsx
{state.mode === 'scientific' && (
{sciButtons.slice(0, 10).map((btn, idx) => ( ))}
)} {/* 原有的基础按钮 */} {buttons.map((btn, idx) => ( ))}
``` - [ ] **Step 3: 添加科学函数区样式** ```css .calc-sci-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; padding: 8px; background: rgba(99, 102, 241, 0.05); border-bottom: 1px solid rgba(51, 65, 85, 0.3); } .calc-sci-row .calc-btn { font-size: 11px; padding: 8px 4px; } [data-theme="dark"] .calc-sci-row { background: rgba(99, 102, 241, 0.08); border-bottom-color: rgba(51, 65, 85, 0.5); } ``` - [ ] **Step 4: 提交** ```bash git add src/pages/Calculator/Calculator.tsx src/pages/Calculator/Calculator.css git commit -m "feat(calculator): add scientific function row" ``` --- ## Task 4: 实现三角函数 **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - [ ] **Step 1: 实现 toRad/fromRad 辅助函数** ```typescript const toRad = (val: number) => state.angleMode === 'DEG' ? val * Math.PI / 180 : val; const fromRad = (val: number) => state.angleMode === 'DEG' ? val * 180 / Math.PI : val; ``` - [ ] **Step 2: 实现 sin/cos/tan 函数** ```typescript const sin = () => { const num = parseFloat(state.current); const result = Math.sin(toRad(num)); setState((prev) => ({ ...prev, current: String(result), overwrite: true, })); }; const cos = () => { const num = parseFloat(state.current); const result = Math.cos(toRad(num)); setState((prev) => ({ ...prev, current: String(result), overwrite: true, })); }; const tan = () => { const num = parseFloat(state.current); const rad = toRad(num); // 检查 tan 的无定义点 (90°, 270°, etc.) const cosVal = Math.cos(rad); if (Math.abs(cosVal) < 1e-10) { message.error(t("calculator.errorTanUndefined")); setState((prev) => ({ ...prev, current: "Error" })); return; } const result = Math.tan(rad); setState((prev) => ({ ...prev, current: String(result), overwrite: true, })); }; ``` - [ ] **Step 3: 提交** ```bash git add src/pages/Calculator/Calculator.tsx git commit -m "feat(calculator): implement trigonometric functions (sin/cos/tan)" ``` --- ## Task 5: 实现对数/指数/幂函数 **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - [ ] **Step 1: 实现 log/ln/x²/x³/√/³√** ```typescript const log10 = () => { const num = parseFloat(state.current); if (num <= 0) { message.error(t("calculator.errorLogNegative")); setState((prev) => ({ ...prev, current: "Error" })); return; } setState((prev) => ({ ...prev, current: String(Math.log10(num)), overwrite: true, })); }; const ln = () => { const num = parseFloat(state.current); if (num <= 0) { message.error(t("calculator.errorLogNegative")); setState((prev) => ({ ...prev, current: "Error" })); return; } setState((prev) => ({ ...prev, current: String(Math.log(num)), overwrite: true, })); }; const square = () => { const num = parseFloat(state.current); setState((prev) => ({ ...prev, current: String(num * num), overwrite: true, })); }; const cube = () => { const num = parseFloat(state.current); setState((prev) => ({ ...prev, current: String(num * num * num), overwrite: true, })); }; const cbrt = () => { const num = parseFloat(state.current); setState((prev) => ({ ...prev, current: String(Math.cbrt(num)), overwrite: true, })); }; const exp = () => { const num = parseFloat(state.current); setState((prev) => ({ ...prev, current: String(Math.exp(num)), overwrite: true, })); }; ``` - [ ] **Step 2: 实现 xʸ 和 x√y(双目运算)** ```typescript const power = () => { setState((prev) => ({ ...prev, previous: prev.current, operator: '^', overwrite: true, history: `${prev.current} ^`, })); }; const nthRoot = () => { setState((prev) => ({ ...prev, previous: prev.current, operator: 'root', overwrite: true, history: `${prev.current} √`, })); }; ``` - [ ] **Step 3: 更新 compute 函数支持 ^ 和 root** ```typescript const compute = (a: string, b: string, op: Operator): string => { const numA = parseFloat(a); const numB = parseFloat(b); if (isNaN(numA) || isNaN(numB)) return "0"; let result = 0; switch (op) { case "+": result = numA + numB; break; case "-": result = numA - numB; break; case "*": result = numA * numB; break; case "/": result = numB === 0 ? NaN : numA / numB; break; case "^": result = Math.pow(numA, numB); break; case "root": result = Math.pow(numA, 1 / numB); break; default: return b; } if (isNaN(result) || !isFinite(result)) return "Error"; const str = result.toString(); return str.length > 15 ? result.toExponential(8) : str; }; ``` - [ ] **Step 4: 提交** ```bash git add src/pages/Calculator/Calculator.tsx git commit -m "feat(calculator): implement logarithm, exponent, and power functions" ``` --- ## Task 6: 实现阶乘/倒数/常数 **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - [ ] **Step 1: 实现阶乘** ```typescript const factorial = () => { const num = parseFloat(state.current); if (num < 0 || !Number.isInteger(num)) { message.error(t("calculator.errorFactorial")); setState((prev) => ({ ...prev, current: "Error" })); return; } if (num > 170) { setState((prev) => ({ ...prev, current: "Error" })); return; } let result = 1; for (let i = 2; i <= num; i++) result *= i; setState((prev) => ({ ...prev, current: String(result), overwrite: true, })); }; ``` - [ ] **Step 2: 实现倒数** ```typescript const reciprocal = () => { const num = parseFloat(state.current); if (num === 0) { message.error(t("calculator.errorDivideByZero")); setState((prev) => ({ ...prev, current: "Error" })); return; } setState((prev) => ({ ...prev, current: String(1 / num), overwrite: true, })); }; ``` - [ ] **Step 3: 实现 π 和 e 常数插入** ```typescript const insertPi = () => { setState((prev) => ({ ...prev, current: String(Math.PI), overwrite: true, })); }; const insertE = () => { setState((prev) => ({ ...prev, current: String(Math.E), overwrite: true, })); }; ``` - [ ] **Step 4: 提交** ```bash git add src/pages/Calculator/Calculator.tsx git commit -m "feat(calculator): implement factorial, reciprocal, and constants" ``` --- ## Task 7: 实现括号支持 **Files:** - Modify: `src/pages/Calculator/Calculator.tsx` - [ ] **Step 1: 实现 openParen/closeParen** ```typescript const openParen = () => { // 简化实现:在历史中记录括号 setState((prev) => ({ ...prev, history: prev.history + "(", parenOpen: true, })); }; const closeParen = () => { setState((prev) => ({ ...prev, history: prev.history + ")", parenOpen: false, })); }; ``` - [ ] **Step 2: 提交** ```bash git add src/pages/Calculator/Calculator.tsx git commit -m "feat(calculator): add parenthesis support" ``` --- ## Task 8: 国际化文本补充 **Files:** - Modify: `src/locales/zh.json` - Modify: `src/locales/en.json` - [ ] **Step 1: 在 zh.json 中添加错误信息** ```json "calculator": { "...": "...", "errorTanUndefined": "tan 在此角度无定义", "errorLogNegative": "对数的真数必须大于 0", "errorFactorial": "阶乘仅支持非负整数", "errorDivideByZero": "不能除以零", "mode": { "basic": "基础", "scientific": "科学" } } ``` - [ ] **Step 2: 在 en.json 中添加错误信息** ```json "calculator": { "...": "...", "errorTanUndefined": "tan is undefined at this angle", "errorLogNegative": "Logarithm requires positive number", "errorFactorial": "Factorial only supports non-negative integers", "errorDivideByZero": "Cannot divide by zero", "mode": { "basic": "Basic", "scientific": "Scientific" } } ``` - [ ] **Step 3: 提交** ```bash git add src/locales/zh.json src/locales/en.json git commit -m "feat(calculator): add i18n error messages and mode labels" ``` --- ## 测试要点 1. **基础四则运算回归**:确保原有功能不受影响 2. **模式切换**:基础 ↔ 科学模式切换正常 3. **三角函数**:sin(30°) = 0.5, cos(60°) = 0.5 4. **DEG/RAD 切换**:同一角度在不同模式下结果不同 5. **对数**:log(100) = 2, ln(e) = 1 6. **错误处理**:负数开方、除以零、阶乘负数都显示 Error 7. **记忆功能**:MC/MR/M+/M− 正确工作 8. **状态指示**:M 标记和 DEG/RAD 指示器正确显示