1.0.5
This commit is contained in:
@@ -11,8 +11,11 @@
|
||||
#fixed-window { width: 1280px; height: 680px; border: 1px solid #e5e7eb; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }
|
||||
.table-container { height: calc(680px - 160px - 64px); overflow-y: auto; }
|
||||
.sticky-header { position: sticky; top: 0; z-index: 10; }
|
||||
@keyframes scan { 0% { top: 0; } 100% { top: 100%; } }
|
||||
.scanning #scan-line { display: block; animation: scan 2s linear infinite; }
|
||||
</style>
|
||||
<script src="/iframe/js/xlsx.full.min.js" language="JavaScript"></script>
|
||||
<script src="/iframe/js/jsQR.min.js" language="JavaScript"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100 font-sans text-sm">
|
||||
<div id="fixed-window" class="bg-gray-50 flex flex-col overflow-hidden mx-auto">
|
||||
@@ -22,6 +25,7 @@
|
||||
<input id="order-uuid" type="text" placeholder="UUID 或者 订单链接" disabled class="flex-grow px-3 py-1.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none" />
|
||||
<button id="import-order-btn" class="px-6 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">添加订单</button>
|
||||
<button id="import-order-file-btn" class="px-6 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">添加订单详情 Excel 文档</button>
|
||||
<button id="open-qr-btn" class="px-6 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">扫描二维码</button>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="font-medium text-gray-700 w-32">立创商城编号导入</span>
|
||||
@@ -68,15 +72,58 @@
|
||||
<button id="clear-list-btn" class="px-6 py-2 border border-red-300 text-red-600 rounded-md hover:bg-red-50">清空列表</button>
|
||||
<button id="batch-save-btn" class="px-8 py-2 bg-blue-600 text-white font-bold rounded-md hover:bg-blue-700 shadow-md">批量入库</button>
|
||||
</div>
|
||||
|
||||
<div id="qr-dialog" class="fixed inset-0 z-50 hidden flex items-center justify-center bg-black bg-opacity-50">
|
||||
<div class="bg-white rounded-lg shadow-xl w-[450px] overflow-hidden">
|
||||
<div class="px-4 py-3 border-b flex justify-between items-center bg-gray-50">
|
||||
<h3 class="font-bold text-gray-700">扫码入库</h3>
|
||||
<button onclick="closeQrDialog()" class="text-gray-400 hover:text-gray-600">×</button>
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-4">
|
||||
<select id="camera-select" class="w-full px-3 py-2 border rounded-md text-sm outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="">正在检测摄像头...</option>
|
||||
</select>
|
||||
|
||||
<div class="relative w-[250px] h-[250px] mx-auto bg-black rounded-lg overflow-hidden border-2 border-gray-300">
|
||||
<video id="qr-video" class="absolute inset-0 w-full h-full object-cover shadow-inner" playsinline></video>
|
||||
<canvas id="qr-canvas" class="hidden"></canvas>
|
||||
<div id="scan-line" class="absolute left-0 right-0 h-0.5 bg-blue-500 opacity-50 shadow-[0_0_8px_rgba(59,130,246,0.8)] hidden"></div>
|
||||
</div>
|
||||
|
||||
<div id="scan-result" class="p-3 bg-blue-50 rounded-md border border-blue-100">
|
||||
<div class="text-sm space-y-1">
|
||||
<p>CID: <span id="res-cid"></span></p>
|
||||
<p>型号: <span id="res-pm"></span></p>
|
||||
<p>数量: <span id="res-qty"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<button id="btn-start-camera" class="py-2 bg-gray-600 text-white rounded hover:bg-gray-700 text-sm">打开摄像头</button>
|
||||
<button id="btn-scan" class="py-2 bg-blue-600 text-white rounded hover:bg-blue-700 text-sm disabled:opacity-50" disabled>扫描二维码</button>
|
||||
<button id="btn-add-to-list" class="py-2 bg-green-600 text-white rounded hover:bg-green-700 text-sm disabled:opacity-50" disabled>加入列表</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const SERVER = eda.sys_Storage.getExtensionUserConfig('server-host') || 'http://localhost:21816/api';
|
||||
const AUTO_RUN = eda.sys_Storage.getExtensionUserConfig('server-auto-run') || true;
|
||||
|
||||
const tableBody = document.getElementById('import-table-body');
|
||||
const listCount = document.getElementById('list-count');
|
||||
const qrDialog = document.getElementById('qr-dialog');
|
||||
const qrVideo = document.getElementById('qr-video');
|
||||
const qrCanvas = document.getElementById('qr-canvas');
|
||||
const cameraSelect = document.getElementById('camera-select');
|
||||
|
||||
let importList = [];
|
||||
let videoStream = null;
|
||||
let currentScanData = null;
|
||||
|
||||
function renderList() {
|
||||
if (importList.length === 0) {
|
||||
@@ -105,6 +152,127 @@
|
||||
listCount.textContent = importList.length;
|
||||
}
|
||||
|
||||
async function updateCameraList() {
|
||||
try {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
||||
cameraSelect.innerHTML = videoDevices.map(d =>
|
||||
`<option value="${d.deviceId}">${d.label || '摄像头 ' + d.deviceId.slice(0, 5)}</option>`
|
||||
).join('');
|
||||
} catch (e) {
|
||||
eda.sys_Message.showToastMessage('无法获取摄像头列表: ' + e.message, ESYS_ToastMessageType.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
window.closeQrDialog = () => {
|
||||
stopCamera();
|
||||
qrDialog.classList.add('hidden');
|
||||
|
||||
document.getElementById('res-cid').textContent = currentScanData.lcscId;
|
||||
document.getElementById('res-pm').textContent = currentScanData.name;
|
||||
document.getElementById('res-qty').textContent = currentScanData.quantity;
|
||||
|
||||
currentScanData = null;
|
||||
};
|
||||
|
||||
function stopCamera() {
|
||||
if (videoStream) {
|
||||
videoStream.getTracks().forEach(track => track.stop());
|
||||
videoStream = null;
|
||||
}
|
||||
qrVideo.parentElement.classList.remove('scanning');
|
||||
}
|
||||
|
||||
document.getElementById('btn-start-camera').onclick = async () => {
|
||||
stopCamera();
|
||||
const deviceId = cameraSelect.value;
|
||||
const constraints = {
|
||||
video: deviceId ? { deviceId: { exact: deviceId } } : { facingMode: 'environment' }
|
||||
};
|
||||
|
||||
try {
|
||||
videoStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
qrVideo.srcObject = videoStream;
|
||||
qrVideo.play();
|
||||
document.getElementById('btn-scan').disabled = false;
|
||||
qrVideo.parentElement.classList.add('scanning');
|
||||
} catch (e) {
|
||||
eda.sys_Message.showToastMessage('启动摄像头失败', ESYS_ToastMessageType.ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('btn-scan').onclick = () => {
|
||||
if (!videoStream) return;
|
||||
|
||||
const ctx = qrCanvas.getContext('2d', { willReadFrequently: true });
|
||||
qrCanvas.width = qrVideo.videoWidth;
|
||||
qrCanvas.height = qrVideo.videoHeight;
|
||||
ctx.drawImage(qrVideo, 0, 0, qrCanvas.width, qrCanvas.height);
|
||||
|
||||
const imageData = ctx.getImageData(0, 0, qrCanvas.width, qrCanvas.height);
|
||||
const code = jsQR(imageData.data, imageData.width, imageData.height);
|
||||
|
||||
if (code) {
|
||||
eda.sys_Log.add(code.data);
|
||||
parseQrData(code.data);
|
||||
} else {
|
||||
eda.sys_Message.showToastMessage('未扫描到有效二维码,请重试', ESYS_ToastMessageType.WARNING);
|
||||
}
|
||||
};
|
||||
|
||||
function parseQrData(data) {
|
||||
try {
|
||||
// 使用正则提取 pc, pm, qty
|
||||
const pcMatch = data.match(/pc:([^,}]+)/);
|
||||
const pmMatch = data.match(/pm:([^,}]+)/);
|
||||
const qtyMatch = data.match(/qty:(\d+)/);
|
||||
|
||||
if (pcMatch) {
|
||||
currentScanData = {
|
||||
lcscId: pcMatch[1].trim().toUpperCase(),
|
||||
name: pmMatch ? pmMatch[1].trim() : '未知型号',
|
||||
quantity: qtyMatch ? parseInt(qtyMatch[1]) : 1,
|
||||
selected: true
|
||||
};
|
||||
|
||||
document.getElementById('res-cid').textContent = currentScanData.lcscId;
|
||||
document.getElementById('res-pm').textContent = currentScanData.name;
|
||||
document.getElementById('res-qty').textContent = currentScanData.quantity;
|
||||
document.getElementById('btn-add-to-list').disabled = false;
|
||||
} else {
|
||||
throw new Error("无效的二维码格式");
|
||||
}
|
||||
} catch (e) {
|
||||
eda.sys_Message.showToastMessage('解析失败: ' + e.message, ESYS_ToastMessageType.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('btn-add-to-list').onclick = async () => {
|
||||
if (!currentScanData) return;
|
||||
|
||||
const btn = document.getElementById('btn-add-to-list');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '处理中...';
|
||||
|
||||
try {
|
||||
const item = { ...currentScanData };
|
||||
await enrichData([item]);
|
||||
importList.push(item);
|
||||
renderList();
|
||||
|
||||
eda.sys_Message.showToastMessage('已加入待入库列表', ESYS_ToastMessageType.SUCCESS);
|
||||
document.getElementById('res-cid').textContent = '等待扫描...';
|
||||
document.getElementById('res-pm').textContent = '等待扫描...';
|
||||
document.getElementById('res-qty').textContent = '等待扫描...';
|
||||
currentScanData = null;
|
||||
} catch (e) {
|
||||
eda.sys_Message.showToastMessage('获取详情失败', ESYS_ToastMessageType.ERROR);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '加入列表';
|
||||
}
|
||||
};
|
||||
|
||||
window.updateQty = (index, val) => { importList[index].quantity = parseInt(val) || 0; };
|
||||
window.removeItem = (index) => { importList.splice(index, 1); renderList(); };
|
||||
|
||||
@@ -114,6 +282,11 @@
|
||||
|
||||
};
|
||||
|
||||
document.getElementById('open-qr-btn').onclick = async () => {
|
||||
qrDialog.classList.remove('hidden');
|
||||
await updateCameraList();
|
||||
};
|
||||
|
||||
document.getElementById('import-order-file-btn').onclick = async () => {
|
||||
try {
|
||||
const file = await eda.sys_FileSystem.openReadFileDialog(['xls', ['xlsx']], false);
|
||||
|
||||
Reference in New Issue
Block a user