From 96c35a4a3c560cb155cbf87237a5ec7d4bdbcea1 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Tue, 14 Apr 2026 01:14:25 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=95=20Add=20scan=20history=20feature?= =?UTF-8?q?=20with=20UI=20and=20localStorage=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- parse-qr/index.html | 227 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/parse-qr/index.html b/parse-qr/index.html index a67881e..4a60c54 100644 --- a/parse-qr/index.html +++ b/parse-qr/index.html @@ -233,6 +233,127 @@ .loading.visible { display: block; } + + .history-section { + margin-top: 30px; + padding-top: 20px; + border-top: 2px solid #f0f4ff; + } + + .history-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + } + + .history-title { + font-weight: 600; + color: #667eea; + font-size: 18px; + } + + .clear-history-btn { + padding: 8px 16px; + background: #ffe6e6; + color: #d63031; + border: none; + border-radius: 6px; + font-size: 14px; + cursor: pointer; + transition: background 0.2s; + } + + .clear-history-btn:hover { + background: #ffd6d6; + } + + .history-list { + max-height: 300px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + + .history-item { + display: flex; + justify-content: space-between; + align-items: flex-start; + padding: 12px; + background: #f8f9ff; + border-radius: 8px; + margin-bottom: 10px; + transition: transform 0.2s; + } + + .history-item:hover { + transform: translateX(4px); + } + + .history-text { + flex: 1; + word-break: break-all; + color: #333; + font-size: 14px; + line-height: 1.5; + margin-right: 10px; + } + + .history-time { + font-size: 12px; + color: #999; + margin-top: 4px; + } + + .history-actions { + display: flex; + gap: 6px; + flex-shrink: 0; + } + + .delete-btn, .copy-history-btn { + padding: 6px 10px; + background: #fff; + border: 1px solid #ffe6e6; + border-radius: 4px; + font-size: 12px; + cursor: pointer; + transition: all 0.2s; + flex-shrink: 0; + } + + .delete-btn { + color: #d63031; + } + + .delete-btn:hover { + background: #d63031; + color: white; + border-color: #d63031; + } + + .copy-history-btn { + color: #667eea; + border-color: #e8eaff; + } + + .copy-history-btn:hover { + background: #667eea; + color: white; + border-color: #667eea; + } + + .copy-history-btn.copied { + background: #00b894; + color: white; + border-color: #00b894; + } + + .empty-history { + text-align: center; + color: #999; + padding: 20px; + font-size: 14px; + } @@ -258,6 +379,14 @@
+ +
+
+
📜 Scan History
+ +
+
+
@@ -280,9 +409,106 @@ const video = document.getElementById('video'); const cameraStatus = document.getElementById('cameraStatus'); const copyBtn = document.getElementById('copyBtn'); + const historyList = document.getElementById('historyList'); + const clearHistoryBtn = document.getElementById('clearHistoryBtn'); + const HISTORY_KEY = 'qr_scan_history'; let stream = null; let scanInterval = null; + function loadHistory() { + const stored = localStorage.getItem(HISTORY_KEY); + return stored ? JSON.parse(stored) : []; + } + + function saveHistory(history) { + localStorage.setItem(HISTORY_KEY, JSON.stringify(history)); + } + + function addToHistory(text) { + const history = loadHistory(); + const existingIndex = history.findIndex(item => item.text === text); + if (existingIndex !== -1) { + history.splice(existingIndex, 1); + } + history.unshift({ text, timestamp: Date.now() }); + if (history.length > 50) { + history.pop(); + } + saveHistory(history); + renderHistory(); + } + + function deleteFromHistory(index) { + const history = loadHistory(); + history.splice(index, 1); + saveHistory(history); + renderHistory(); + } + + function clearHistory() { + localStorage.removeItem(HISTORY_KEY); + renderHistory(); + } + + function renderHistory() { + const history = loadHistory(); + if (history.length === 0) { + historyList.innerHTML = '
No scan history yet
'; + return; + } + historyList.innerHTML = history.map((item, index) => { + const date = new Date(item.timestamp); + const timeStr = date.toLocaleString(); + const displayText = item.text.length > 100 ? item.text.substring(0, 100) + '...' : item.text; + return ` +
+
+
${escapeHtml(displayText)}
+
${timeStr}
+
+
+ + +
+
+ `; + }).join(''); + historyList.querySelectorAll('.delete-btn').forEach(btn => { + btn.addEventListener('click', (e) => { + const index = parseInt(e.target.dataset.index); + deleteFromHistory(index); + }); + }); + historyList.querySelectorAll('.copy-history-btn').forEach(btn => { + btn.addEventListener('click', (e) => { + const index = parseInt(e.target.dataset.index); + const text = history[index].text; + navigator.clipboard.writeText(text).then(() => { + e.target.textContent = '✓ Copied!'; + e.target.classList.add('copied'); + setTimeout(() => { + e.target.textContent = '📋 Copy'; + e.target.classList.remove('copied'); + }, 2000); + }); + }); + }); + } + + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + clearHistoryBtn.addEventListener('click', () => { + if (confirm('Are you sure you want to clear all scan history?')) { + clearHistory(); + } + }); + + renderHistory(); + dropZone.addEventListener('click', () => fileInput.click()); dropZone.addEventListener('dragover', (e) => { @@ -371,6 +597,7 @@ function showResult(text) { resultText.textContent = text; result.classList.add('visible'); + addToHistory(text); } function showError(message) {