Initial commit: caddy static file server workspace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
144
caddy/www/instascan_invoice_parser.html
Normal file
144
caddy/www/instascan_invoice_parser.html
Normal file
@@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-TW">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>QR Code Scanner</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
video {
|
||||
border: 2px solid #000;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
select {
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
}
|
||||
#result {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
word-break: break-word;
|
||||
max-width: 90%;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<script src="https://cdn.rawgit.com/schmich/instascan-builds/master/instascan.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>QR Code Scanner</h1>
|
||||
<select id="cameraSelector"></select>
|
||||
<video id="preview"></video>
|
||||
<div id="result">請將 QR Code 對準攝影機。</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
const videoElement = document.getElementById('preview');
|
||||
const resultDiv = document.getElementById('result');
|
||||
const cameraSelector = document.getElementById('cameraSelector');
|
||||
let scanner = new Instascan.Scanner({ video: videoElement });
|
||||
let cameras = [];
|
||||
|
||||
// 瀏覽器支援檢查
|
||||
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||||
alert('您的瀏覽器不支援攝影機訪問。請使用最新版本的 Chrome 或 Safari。');
|
||||
return;
|
||||
}
|
||||
|
||||
alert('開始檢查攝影機是否可用...');
|
||||
|
||||
// 列出所有攝影機
|
||||
Instascan.Camera.getCameras()
|
||||
.then(function (availableCameras) {
|
||||
if (availableCameras.length > 0) {
|
||||
cameras = availableCameras;
|
||||
cameraSelector.innerHTML = cameras
|
||||
.map((camera, index) => `<option value="${index}">${camera.name || `攝影機 ${index + 1}`}</option>`)
|
||||
.join('');
|
||||
alert(`找到 ${cameras.length} 個攝影機,請選擇要使用的攝影機。`);
|
||||
startScanner(cameras[0]); // 預設啟用第一個攝影機
|
||||
} else {
|
||||
alert('未找到攝影機。');
|
||||
resultDiv.textContent = '未找到攝影機。';
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert(`無法啟用攝影機:${error.message}`);
|
||||
resultDiv.textContent = `無法啟用攝影機:${error.message}`;
|
||||
});
|
||||
|
||||
// 改變選擇的攝影機
|
||||
cameraSelector.addEventListener('change', function () {
|
||||
const selectedCameraIndex = parseInt(cameraSelector.value, 10);
|
||||
if (cameras[selectedCameraIndex]) {
|
||||
startScanner(cameras[selectedCameraIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
// 啟動掃描器
|
||||
function startScanner(camera) {
|
||||
scanner.stop().then(() => {
|
||||
scanner.start(camera).catch(function (error) {
|
||||
alert(`無法啟動攝影機:${error.message}`);
|
||||
resultDiv.textContent = `無法啟動攝影機:${error.message}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 掃描 QR Code
|
||||
scanner.addListener('scan', function (content) {
|
||||
alert(`識別到的 QR Code:${content}`);
|
||||
resultDiv.textContent = `識別到的 QR Code:${content}`;
|
||||
parseQRCode(content);
|
||||
});
|
||||
|
||||
// 解析 QR Code
|
||||
function parseQRCode(qrCode) {
|
||||
try {
|
||||
qrCode = qrCode.trim();
|
||||
const invoiceNumber = qrCode.slice(0, 10);
|
||||
const issueDate = qrCode.slice(10, 17);
|
||||
const year = parseInt(issueDate.slice(0, 3)) + 1911;
|
||||
const month = issueDate.slice(3, 5);
|
||||
const day = issueDate.slice(5, 7);
|
||||
const formattedDate = `${year}-${month}-${day}`;
|
||||
const randomCode = qrCode.slice(17, 21);
|
||||
const untaxedAmountHex = qrCode.slice(21, 29);
|
||||
const untaxedAmount = parseInt(untaxedAmountHex, 16);
|
||||
const totalAmountHex = qrCode.slice(29, 37);
|
||||
const totalAmount = parseInt(totalAmountHex, 16);
|
||||
const buyerTaxId = qrCode.slice(37, 45);
|
||||
const sellerTaxId = qrCode.slice(45, 53);
|
||||
const encryptedCode = qrCode.slice(53, 77);
|
||||
|
||||
resultDiv.innerHTML = `
|
||||
<strong>發票號碼:</strong> ${invoiceNumber}<br>
|
||||
<strong>開立日期:</strong> ${formattedDate}<br>
|
||||
<strong>隨機碼:</strong> ${randomCode}<br>
|
||||
<strong>未稅金額:</strong> ${untaxedAmount}<br>
|
||||
<strong>總計金額:</strong> ${totalAmount}<br>
|
||||
<strong>買方統一編號:</strong> ${buyerTaxId}<br>
|
||||
<strong>賣方統一編號:</strong> ${sellerTaxId}<br>
|
||||
<strong>加密驗證資訊:</strong> ${encryptedCode}<br>
|
||||
`;
|
||||
} catch (error) {
|
||||
alert(`解析 QR Code 內容失敗:${error.message}`);
|
||||
resultDiv.textContent = `解析 QR Code 內容失敗:${error.message}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user