第一段:Day 12 完成了商務折扣、正負號切換與雙零/三零快速輸入,使用者在大額交易中已能流暢地完成單步計算。然而,真實的商務場景往往需要跨步驟保存中間結果——計算小計後切換幣別、確認稅額後再加總——若每次都重新輸入,不僅效率低落,更容易累積手誤。
第二段:今日聚焦於五鍵完整記憶體系統(MC、MR、M+、M‑、MS)的實作,以及螢幕左上角的 [M] 發光指示燈。指示燈在記憶值非零時點亮,並以 CSS 過渡動畫呈現柔和的光暈浮現效果;點擊指示燈即可直接喚醒 MR,讓記憶體操作從「找按鍵」升級為「看一眼即能回應」的直觀體驗。
線上體驗 Live Demo:https://410355-collab.github.io/Premium-Business-Calculator/
(強烈建議使用 iPhone Safari 或 Android 手機開啟體驗!)
GitHub 開源倉庫:https://github.com/410355-collab/Premium-Business-Calculator
歡迎提出建議與指導
(開發環境:Google Antgravity IDE + Google Gemini 協同開發)
傳統計算機的記憶體功能雖然存在,但多數實作僅停在單一 M 變數,且沒有明確的視覺狀態。使用者在快節奏的商務計算中無法確認「我剛才有存過嗎?」,導致誤按 MR 後得到「MEMORY EMPTY」的回應,打斷思路。
當記憶值存在時,若 UI 沒有任何提示,使用者必須記住自己的操作步驟,增加認知負荷。在多步驟財務試算中,這種無狀態的設計是記憶體功能利用率低落的根本原因。
// script.js 第 134 行
let memoryValue = 0;
memoryValue 以 JS number 初始化為 0,但在 M+ 與 M‑ 的累加/扣除運算中,立即轉換為 math.bignumber 以確保大數精度,避免 JS 原生浮點誤差在高額財務計算中擴大。
// script.js – 記憶體鍵位物件
'mem_clear': { label: 'MC', action: 'memoryClear', type: 'accent-mem small-text', category: 'mem', name: 'MC' },
'mem_recall': { label: 'MR', action: 'memoryRecall', type: 'accent-mem small-text', category: 'mem', name: 'MR' },
'mem_add': { label: 'M+', action: 'memoryAdd', type: 'accent-mem small-text', category: 'mem', name: 'M+' },
'mem_sub': { label: 'M-', action: 'memorySub', type: 'accent-mem small-text', category: 'mem', name: 'M-' },
'mem_store': { label: 'MS', action: 'memoryStore', type: 'accent-mem small-text', category: 'mem', name: 'MS' },
五鍵均隸屬 category: 'mem',在 renderMainKeyboard 時統一排入「記憶體區」,視覺上以 accent-mem 配色與數字鍵明確區隔。
// script.js 第 1186 ~ 1230 行
// MC – 清零並更新指示燈
} else if (funcName === 'memoryClear') {
memoryValue = 0;
updateMemoryIndicator();
showToastMsg(I18N[currentLang].toastMemCleared);
// MR – 讀取並插入當前運算式
} else if (funcName === 'memoryRecall') {
if (memoryValue === 0) {
showToastMsg(I18N[currentLang].toastNoMem);
} else {
const mStr = formatNumber(memoryValue).replace(/,/g, '');
if (isEvaluated || currentInput === "0") {
currentInput = mStr;
isEvaluated = false;
} else {
currentInput += mStr;
}
cursorPos = null;
animType = 'append';
}
// M+ – 累加(bignumber 精確運算)
} else if (funcName === 'memoryAdd') {
try {
let val = safeEvaluate(autoCloseParentheses(currentInput));
if (math.isBigNumber(val) && val.isFinite()) {
memoryValue = math.add(math.bignumber(memoryValue || 0), val);
updateMemoryIndicator();
showToastMsg(I18N[currentLang].toastMemStored);
}
} catch(e) {}
// M- – 扣除(bignumber 精確運算)
} else if (funcName === 'memorySub') {
try {
let val = safeEvaluate(autoCloseParentheses(currentInput));
if (math.isBigNumber(val) && val.isFinite()) {
memoryValue = math.subtract(math.bignumber(memoryValue || 0), val);
updateMemoryIndicator();
showToastMsg(I18N[currentLang].toastMemStored);
}
} catch(e) {}
// MS – 直接覆寫記憶值
} else if (funcName === 'memoryStore') {
try {
let val = safeEvaluate(autoCloseParentheses(currentInput));
if (math.isBigNumber(val) && val.isFinite()) {
memoryValue = val;
updateMemoryIndicator();
showToastMsg(I18N[currentLang].toastMemStored);
}
} catch(e) {}
autoCloseParentheses 前置處理M+、M‑、MS 在求值前均先呼叫 autoCloseParentheses(currentInput),確保使用者若未關閉括號仍能正確計算當前值,防止因括號未閉合而靜默失敗。
index.html 第 168 行)<div class="mem-indicator" id="mem-indicator" title="Memory (M)">M</div>
純文字 M 搭配 title 提示,不使用圖示,保持輕量與可讀性。
style.css 第 569 ~ 607 行).mem-indicator {
font-size: 11px;
font-weight: 900;
letter-spacing: 0.5px;
padding: 2px 7px;
border-radius: 8px;
color: var(--text-muted);
background: rgba(255, 255, 255, 0.04);
border: 0.5px solid rgba(255, 255, 255, 0.12);
opacity: 0;
transform: scale(0.85);
transition: opacity 0.22s ease, transform 0.22s var(--ios-spring-snappy),
color 0.2s, background 0.2s, border-color 0.2s, box-shadow 0.2s;
cursor: pointer;
pointer-events: none;
}
.mem-indicator.active {
opacity: 1;
transform: scale(1);
color: var(--accent-main);
background: color-mix(in srgb, var(--accent-main) 18%, transparent);
border-color: color-mix(in srgb, var(--accent-main) 45%, transparent);
box-shadow: 0 0 12px color-mix(in srgb, var(--accent-main) 35%, transparent);
pointer-events: auto;
}
.mem-indicator.active:active {
transform: scale(0.92);
}
亮色模式下同步切換為 #0284c7 藍系配色,與深色模式的 --accent-main 保持語意一致。
script.js 第 518 ~ 523 行)function updateMemoryIndicator() {
const memEl = document.getElementById('mem-indicator');
if (!memEl) return;
const hasMem = (memoryValue !== 0 && memoryValue !== '0'
&& memoryValue !== null && memoryValue !== undefined);
memEl.classList.toggle('active', !!hasMem);
}
四重非零條件防止 bignumber(0)、字串 '0'、null、undefined 造成指示燈誤亮。
script.js 第 4442 ~ 4447 行)const memBadge = document.getElementById('mem-indicator');
if (memBadge) {
attachSmartTap(memBadge, () => {
instantInput(null, 'memoryRecall');
});
}
使用 attachSmartTap 而非原生 addEventListener('click'),可自動繼承專案的 haptic 回饋與防雙觸邏輯,確保在手機快速操作時不會觸發兩次 MR。
script.jsmemoryValue(第 134 行)updateMemoryIndicator(第 518 ~ 523 行):四重非零檢測,單行 classList.toggle
attachSmartTap 點擊綁定(第 4442 ~ 4447 行)color-mix 而非 hardcode 色碼color-mix(in srgb, var(--accent-main) 18%, transparent) 讓指示燈的背景色完全隨主題的 --accent-main 動態調整,使用者切換主題色時指示燈自動跟隨,無需另外為每個主題寫覆蓋規則。
pointer-events: none 在非 active 狀態指示燈在記憶值為 0 時 opacity: 0 且 pointer-events: none,確保使用者不會誤觸到一個視覺上「不存在」的元素。只有在 active 時才切換為 pointer-events: auto。
math.bignumber 的必要性M+ 累加若使用原生 JS 加法,在數值超過 2⁵³ 時會喪失精度。在商務場景中,億元以上的資產合計若出現浮點偏差,是無法被接受的錯誤,因此強制使用 mathjs 的 bignumber 運算。
autoCloseParentheses 的保護意義使用者在輸入 12*(5+3 時若直接按 M+ 而未閉合括號,safeEvaluate 會拋出例外。前置 autoCloseParentheses 自動補全為 12*(5+3),讓記憶體操作能正確捕獲當前值,而非靜默失敗。
Day 13 完成了五鍵記憶體系統與 [M] 指示燈的完整閉環。使用者現在可以在多步驟商務計算中安全地暫存、累加、扣除與讀取中間結果;[M] 指示燈以最低的視覺干擾、最直觀的互動方式告知記憶狀態,點擊即觸發 MR,讓記憶體操作真正融入快節奏的計算流程。
Day 14 將引入 sin、cos、tan、ln、log、xʸ、x!、|x|、1/x、π、e 等完整科學函數模組,每個函數均透過 mathjs 求值並支援角度/弧度雙模式。同時,我們將推出智慧括號平衡演算法,能精確區分「未閉合括號」、「除以零」與「語法錯誤」三種錯誤類型並給予對應提示,讓科學計算的錯誤診斷體驗大幅升級。