iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Build on Google AI

液態玻璃美學:結合 Gemini 與 Antigravity IDE,打造 120FPS、多視窗比例與網格順延推擠佈局的強大商務運算終端系列 第 18

Day 18:【高階商務計算機】算完帳不會忘記用途——即時記帳分類氣泡與歷史二分法隔離體系

  • 分享至 

  • xImage
  •  

昨天我們實作了跨國雙幣別對衝連動、自訂匯率覆寫面板與具備斷網防禦的快照同步引擎,徹底穩固了計算機在極端網路與商務環境下的多幣別換算能力。然而,當我們在海外差旅採購或日常生活頻繁操作計算機時,另一個真實痛點悄然浮現:當我們在超商買完咖啡、搭完計程車或算完一筆餐費,敲下等於鍵得出答案的那一秒,是個人記憶最鮮明、最適合記帳的黃金時刻;如果在此刻強迫跳出繁重的彈窗填表,會瞬間打斷連續計算的心流;但如果什麼都不做,到了晚上回到飯店打開記帳本,面對滿滿一整頁只有數字的歷史紀錄,根本想不起來哪一筆是晚餐、哪一筆只是隨手驗算的算式。

為了讓商務計算與日常記帳達到無感的自然共生,今天我們實作計算完成即時記帳分類氣泡與歷史紀錄二分法隔離體系。當使用者按下等於求出答案的瞬間,頂部會自動彈出一道輕柔懸浮的分類膠囊,提供四點五秒靜默倒數的一鍵直覺標記;更重要的是,系統建立起嚴格的二分法過濾機制,將純計算驗算草稿與真實生活開銷徹底隔離,搭配歷史抽屜中的今日動態總計,讓記帳如呼吸般自然隨行。


專案正式上線體驗與開源倉庫

線上體驗 Live Demo:https://410355-collab.github.io/Premium-Business-Calculator/
(強烈建議使用 iPhone Safari 或 Android 手機開啟體驗!)

GitHub 開源倉庫:https://github.com/410355-collab/Premium-Business-Calculator
歡迎提出建議與指導
(開發環境:Google Antigravity IDE + Google Gemini 協同開發)


一、技術痛點:計算情境割裂與歷史帳目污染

在日常生活與跨國商務的運算行為中,傳統工具存在著兩大體驗割裂:

第一個問題是記帳行為與計算情境嚴重脫節。多數人的日常習慣是先打開計算機算好含稅價或折讓總額,然後再手動切換至專屬的記帳應用程式,重新鍵入數字、選擇分類、備註明細。這種跨應用跳轉的繁瑣摩擦力,往往導致使用者堅持不到三天就徹底放棄;而一般計算機內建的歷史紀錄清單,又充斥著無分類的單純數字,久而久之變成了一堆毫無商業價值的雜亂數字堆疊。

第二個問題是純運算草稿與正式開銷帳目的混雜污染。使用者每天使用計算機,除了記錄真正的生活支出(如飲食、購物、交通)之外,有超過半數的操作只是在進行臨時驗算、單位折算或工作估價。如果計算機把每一次按下等於的紀錄都無差別歸納為開銷,那麼歷史帳目中的累計總額將嚴重失真。要建立專業級的商務體驗,系統必須具備即時歸類機制與明確的資料二分法架構。

二、架構設計:四點五秒懸浮氣泡與二分法資料流

為了解決操作繁瑣與帳目混雜的難題,我們構建了無感介入的智慧記帳體系:

四點五秒黃金懸浮氣泡管線:
在運算核心觸發求值完成的瞬間,系統將計算結果即時寫入歷史隊列,並在頂部狀態列下方動態喚醒懸浮分類氣泡。氣泡內部提供食、購物、行、其他等直覺標籤,並啟動四點五秒靜默倒數定時器。使用者只需順手輕點一下,即可將當前最新紀錄就地打上分類標記;若使用者不想記帳,只需繼續鍵入下一個數字或靜置數秒,氣泡便自動淡出收折,完全不阻礙連續計算的心流。

歷史紀錄二分法隔離體系:
我們在歷史抽屜中建立了獨立的二分法檢視模式,將資料清晰劃分為記帳模式與商務模式。商務模式專門收納未標記特定生活分類的日常純計算與商務驗算草稿;而記帳模式則專屬過濾出食、購物等真正產生的生活費用,兩者互不干擾、權責分明,從源頭杜絕了運算草稿污染個人財務總帳的弊病。

今日總計動態摘要卡片:
在歷史側邊選單頂端,我們設計了一塊自適應動態摘要卡片。卡片具備日期即時感知能力,能夠自動比對各筆紀錄的時間戳記,動態彙總今日所屬模式下的即時開銷總額與有效筆數。當使用者在記帳與商務分頁間切換時,卡片名稱與累計金額秒速平滑響應,為商務人士提供一目了然的每日收支儀表板。

三、核心代碼精華:script.js

以下為即時分類氣泡喚醒定時器、分類覆寫與二分法資料過濾的關鍵實作:

// 計算結束瞬間喚醒四點五秒即時記帳分類氣泡
function showCategoryBubble() {
    if (!isCategoryBubbleEnabled) return;
    const bubble = document.getElementById('category-bubble');
    const headerArea = document.getElementById('header-area');
    if (!bubble) return;
    if (categoryBubbleTimer) {
        clearTimeout(categoryBubbleTimer);
        categoryBubbleTimer = null;
    }

    // 讀取上一筆紀錄之分類狀態並高亮對應標籤
    const currentCat = (calcHistory.length > 0 && calcHistory[0].category) || 'business';
    bubble.querySelectorAll('.cat-chip').forEach(chip => {
        chip.classList.toggle('active', chip.dataset.cat === currentCat);
    });

    bubble.classList.add('show');
    if (headerArea) headerArea.classList.add('bubble-active');
    
    // 設置四點五秒自動淡出定時器,不干擾下一輪計算
    categoryBubbleTimer = setTimeout(() => {
        hideCategoryBubble();
    }, 4500);
}

// 點擊分類標籤立即覆寫最新一筆歷史之分類
function setLatestHistoryCategory(category) {
    triggerVibration();
    if (calcHistory.length > 0) {
        calcHistory[0].category = category;
        saveHistoryStorage();
        updateHistoryButtonUI();
    }
    const bubble = document.getElementById('category-bubble');
    if (bubble) {
        bubble.querySelectorAll('.cat-chip').forEach(chip => {
            chip.classList.toggle('active', chip.dataset.cat === category);
        });
    }
    setTimeout(() => {
        hideCategoryBubble();
    }, 320);
}

接著是歷史選單中,依據二分法模式動態過濾並統計今日開銷的渲染引擎:

let currentHistoryMode = 'accounting'; // 支援 'accounting' (個人生活記帳) 與 'business' (商務純運算)

function renderHistory() {
    updateHistoryButtonUI();
    updateCategorySummaryVisibility();
    const container = document.getElementById('history-container');
    const todayAmountEl = document.getElementById('history-today-amount');
    const totalCountEl = document.getElementById('history-total-count');
    const summaryTitleEl = document.getElementById('history-summary-title');

    // 動態更新摘要卡片標題
    if (summaryTitleEl) {
        summaryTitleEl.textContent = currentHistoryMode === 'accounting'
            ? (currentLang === 'zh' ? '記帳總計' : "TODAY'S EXPENSE")
            : (currentLang === 'zh' ? '商務總額' : "TODAY'S BIZ TOTAL");
    }

    if (!calcHistory || calcHistory.length === 0) {
        if (todayAmountEl) todayAmountEl.textContent = `0.00 ${currentFrom}`;
        if (totalCountEl) totalCountEl.textContent = (I18N[currentLang].historyListCount || "{count} records").replace('{count}', '0');
        container.innerHTML = `<div class="empty-history">${I18N[currentLang].noHistory}</div>`;
        return;
    }

    // 核心二分法過濾:嚴格區分生活記帳與純商務計算
    const filteredHistory = calcHistory.map((item, originalIndex) => ({ ...item, originalIndex })).filter(item => {
        const cat = item.category || 'business';
        if (currentHistoryMode === 'accounting') {
            return cat !== 'business';
        } else {
            return cat === 'business';
        }
    });

    // 動態累計今日金額總計
    const todayStr = new Date().toDateString();
    let todayTotal = 0;
    filteredHistory.forEach(item => {
        const refDate = item.addedAt || item.timestamp;
        const itemDateStr = refDate ? new Date(refDate).toDateString() : todayStr;
        if (itemDateStr === todayStr) {
            const resVal = typeof item.result === 'number' ? item.result : parseFloat(String(item.result).replace(/,/g, '')) || 0;
            todayTotal += isNaN(resVal) ? 0 : resVal;
        }
    });

    if (todayAmountEl) todayAmountEl.textContent = `${formatNumber(todayTotal)} ${currentFrom}`;
    if (totalCountEl) totalCountEl.textContent = (I18N[currentLang].historyListCount || "{count} records").replace('{count}', filteredHistory.length);

    if (filteredHistory.length === 0) {
        const emptyMsg = currentHistoryMode === 'accounting' 
            ? (currentLang === 'zh' ? '無記帳紀錄' : 'NO ACCOUNTING RECORDS')
            : (currentLang === 'zh' ? '無商務紀錄' : 'NO BUSINESS RECORDS');
        container.innerHTML = `<div class="empty-history">${emptyMsg}</div>`;
        return;
    }

    // 渲染帶有分類標籤、外幣對照與時間戳記之卡片清單
    container.innerHTML = filteredHistory.map((item) => {
        const cat = item.category || 'business';
        const catEmoji = getCategoryEmoji(cat);
        const catName = getCategoryName(cat);
        const timeStr = item.timestamp ? new Date(item.timestamp).toLocaleString(currentLang === 'zh' ? 'zh-TW' : 'en-US', {
            hour: 'numeric',
            minute: '2-digit'
        }) : '';
        const currFrom = item.currencyFrom || currentFrom;
        return `
            <div class="history-item" data-index="${item.originalIndex}">
                <div class="history-item-top">
                    <span class="history-cat-badge">${catEmoji} ${catName}</span>
                    <span class="history-time">${timeStr}</span>
                </div>
                <div class="history-formula">${item.formula} =</div>
                <div class="history-result">${formatNumber(item.result)} <span class="curr-unit">${currFrom}</span></div>
            </div>
        `;
    }).join('');
}

四、深度調校剖析:無壓迫互動與微交互細節

在實作記帳氣泡與摘要卡片的互動體驗時,我們融入了多項細膩的工程調校:

非阻塞式輸入防護機制:
在使用者進行連續快速計算時,若氣泡一直佔據螢幕甚至攔截點擊,會引起使用者的反感。我們在輸入分發器的所有入口中加入了主動抑制邏輯:只要使用者開始按下鍵盤鍵入下一個算式,系統便立即呼叫隱藏氣泡函式,並在零點三秒內優雅滑出退場。這確保了記帳功能永遠只是安靜貼心的輔助,絕不喧賓奪主。

歷史彈窗就地二次編輯:
即便使用者在當下錯過了四點五秒的懸浮氣泡,也不必擔心紀錄無法歸類。當使用者點開歷史抽屜中的任一歷史卡片時,系統會喚醒底部互動面板,允許使用者隨時重新選擇分類標籤、複製算式或載入數值回主螢幕,提供百分之百的後悔藥與事後整理空間。

單純開銷與商務驗算嚴格隔絕:
許多人在出差時需要計算採購折扣或稅率試算,這些過程數字絕不能被算進個人實際支出的總額中。透過二分法的底層資料架構,所有未被特別點選生活分類的紀錄皆預設為商務計算,既保留了隨時回查算式的便利性,又確保了記帳分頁所統計出的今日總額百分之百乾淨真實。

五、總結

今天我們成功實作了計算完成即時記帳分類氣泡、歷史二分法隔離模式與今日總計動態摘要卡片。透過捕捉計算完成瞬間的黃金注意力,大幅降低了日常記帳的心理負擔;搭配嚴謹的資料二分架構,讓商務運算與個人財務在同一個工具中各司其職、井然有序。

然而,當我們辛辛苦苦累積了數十筆甚至上百筆差旅開銷與商務核銷紀錄後,新的難題又出現了:難道我們要一筆一筆手動抄寫回報給會計?如何將這些帶有幣別、分類與時間戳記的帳目,一鍵變成正規的試算表文件發送至通訊軟體?明天我們將為大家帶來終極的資料匯出解決方案!

明日預告

辛辛苦苦在手機上記好了一整趟出差的各項開銷,回到公司難道還要一筆一筆手動敲進電腦報銷嗎?如果能不靠後端伺服器、在手機本機端一秒自動生成正規且排除雜項的試算表,甚至直接分享至通訊軟體,會有多輕鬆?明天我們將揭開離線試算表生成的奧秘,敬請期待!


上一篇
Day 17:【高階商務計算機】出國換匯率不再斷網就癱瘓——跨國雙幣對衝連動、自訂匯率覆寫與斷網降級快照
下一篇
Day 19:【高階商務計算機】手抄開銷報銷太崩潰?自動記帳——試算表匯出、統計與原生分享
系列文
液態玻璃美學:結合 Gemini 與 Antigravity IDE,打造 120FPS、多視窗比例與網格順延推擠佈局的強大商務運算終端21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言