fix bug
This commit is contained in:
parent
3118185b31
commit
b0f13bc3f1
@ -267,7 +267,8 @@
|
||||
let detail = { name: '-', value: '-', footprint: '-', brand: '-', designatorStr: '手动添加', selected: false };
|
||||
|
||||
if (devs[0]) {
|
||||
const infoRes = await eda.sys_ClientUrl.request('https://client/api/v2/devices/' + devs[0].uuid, 'GET', null, {
|
||||
const EDA_HOST = eda.sys_Environment.isClient() ? 'https://client' : 'https://pro.lceda.cn';
|
||||
const infoRes = await eda.sys_ClientUrl.request(EDA_HOST + '/api/v2/devices/' + devs[0].uuid, 'GET', null, {
|
||||
headers: { path: '0819f05c4eef4c71ace90d822a990e87' },
|
||||
});
|
||||
const info = (await infoRes.json()).result;
|
||||
@ -295,8 +296,8 @@
|
||||
return eda.sys_Message.showToastMessage('请先勾选要出库的器件', ESYS_ToastMessageType.WARNING);
|
||||
}
|
||||
|
||||
const SERVER = (await eda.sys_Storage.getExtensionUserConfig('server-host')) || 'http://localhost:21816';
|
||||
const AUTO_RUN = (await eda.sys_Storage.getExtensionUserConfig('server-auto-run')) || true;
|
||||
const SERVER = (await eda.sys_Storage.getExtensionUserConfig('server-host')) ?? 'http://localhost:21816';
|
||||
const AUTO_RUN = (await eda.sys_Storage.getExtensionUserConfig('server-auto-run')) ?? true;
|
||||
let successCount = 0;
|
||||
let failItems = [];
|
||||
|
||||
|
||||
@ -1,38 +1,83 @@
|
||||
<!doctype html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>器件入库 - LEYE</title>
|
||||
<link href="/iframe/css/index.css" rel="stylesheet" />
|
||||
<style>
|
||||
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
||||
#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; }
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
#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>
|
||||
<script src="/iframe/js/qrcode.min.js" language="JavaScript"></script>
|
||||
<script src="/iframe/js/mqtt.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">
|
||||
</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">
|
||||
<header class="flex-shrink-0 bg-white border-b border-gray-200 shadow-sm p-4 space-y-3">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="font-medium text-gray-700 w-32">立创商城订单导入</span>
|
||||
<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" />
|
||||
<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="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>
|
||||
<input id="single-cid" type="text" placeholder="CID" class="w-64 px-3 py-1.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none" />
|
||||
<input id="single-qty" type="number" placeholder="数量" class="w-32 px-3 py-1.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none" />
|
||||
<input
|
||||
id="single-cid"
|
||||
type="text"
|
||||
placeholder="CID"
|
||||
class="w-64 px-3 py-1.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
/>
|
||||
<input
|
||||
id="single-qty"
|
||||
type="number"
|
||||
placeholder="数量"
|
||||
class="w-32 px-3 py-1.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
/>
|
||||
<button id="import-cid-btn" class="px-6 py-1.5 bg-green-600 text-white rounded-md hover:bg-green-700 transition">添加器件</button>
|
||||
</div>
|
||||
</header>
|
||||
@ -42,9 +87,7 @@
|
||||
<button id="select-all" class="px-3 py-1 bg-white border border-gray-300 rounded hover:bg-gray-100 text-xs">全选</button>
|
||||
<button id="select-reverse" class="px-3 py-1 bg-white border border-gray-300 rounded hover:bg-gray-100 text-xs">反选</button>
|
||||
</div>
|
||||
<div class="text-gray-500 text-xs">
|
||||
待入库项:<span id="list-count" class="font-bold text-blue-600">0</span>
|
||||
</div>
|
||||
<div class="text-gray-500 text-xs">待入库项:<span id="list-count" class="font-bold text-blue-600">0</span></div>
|
||||
</div>
|
||||
|
||||
<main class="flex-grow bg-white overflow-hidden m-3 border rounded-lg shadow-inner">
|
||||
@ -64,7 +107,9 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="import-table-body" class="bg-white divide-y divide-gray-200 text-xs">
|
||||
<tr id="empty-row"><td colspan="9" class="text-center py-20 text-gray-400">列表为空,请从上方导入数据</td></tr>
|
||||
<tr id="empty-row">
|
||||
<td colspan="9" class="text-center py-20 text-gray-400">列表为空,请从上方导入数据</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -72,7 +117,9 @@
|
||||
|
||||
<div class="flex-shrink-0 p-4 bg-white border-t border-gray-200 flex justify-end space-x-3 shadow-lg">
|
||||
<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>
|
||||
<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">
|
||||
@ -90,7 +137,10 @@
|
||||
<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
|
||||
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">
|
||||
@ -103,18 +153,26 @@
|
||||
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<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 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');
|
||||
@ -136,7 +194,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
tableBody.innerHTML = importList.map((item, index) => `
|
||||
tableBody.innerHTML = importList
|
||||
.map(
|
||||
(item, index) => `
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-2 text-center"><input type="checkbox" class="row-checkbox" data-index="${index}" ${item.selected ? 'checked' : ''}></td>
|
||||
<td class="px-3 py-2 font-medium">${item.name || '-'}</td>
|
||||
@ -152,17 +212,19 @@
|
||||
<button onclick="removeItem(${index})" class="text-red-500 hover:text-red-700">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
`,
|
||||
)
|
||||
.join('');
|
||||
listCount.textContent = importList.length;
|
||||
}
|
||||
|
||||
async function updateCameraList() {
|
||||
try {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
||||
let options = videoDevices.map(d =>
|
||||
`<option value="${d.deviceId}">${d.label || '摄像头 ' + d.deviceId.slice(0, 5)}</option>`
|
||||
).join('');
|
||||
const videoDevices = devices.filter((device) => device.kind === 'videoinput');
|
||||
let options = videoDevices
|
||||
.map((d) => `<option value="${d.deviceId}">${d.label || '摄像头 ' + d.deviceId.slice(0, 5)}</option>`)
|
||||
.join('');
|
||||
|
||||
options += `<option value="WEBSOCKET_MODE">WebSocket 远程扫码</option>`;
|
||||
cameraSelect.innerHTML = options;
|
||||
@ -185,12 +247,12 @@
|
||||
|
||||
function stopCamera() {
|
||||
if (videoStream) {
|
||||
videoStream.getTracks().forEach(track => track.stop());
|
||||
videoStream.getTracks().forEach((track) => track.stop());
|
||||
videoStream = null;
|
||||
}
|
||||
|
||||
if (mqttClient) {
|
||||
console.log("正在断开 MQTT 远程连接...");
|
||||
console.log('正在断开 MQTT 远程连接...');
|
||||
mqttClient.end(true); // 强制关闭连接
|
||||
mqttClient = null;
|
||||
}
|
||||
@ -199,7 +261,7 @@
|
||||
const oldQr = document.getElementById('remote-qr-canvas');
|
||||
if (oldQr) oldQr.remove();
|
||||
|
||||
if(qrVideo.parentElement) {
|
||||
if (qrVideo.parentElement) {
|
||||
qrVideo.parentElement.classList.remove('scanning');
|
||||
}
|
||||
}
|
||||
@ -233,7 +295,7 @@
|
||||
eda.sys_Message.showToastMessage('远程扫码成功', ESYS_ToastMessageType.INFO);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("MQTT数据解析失败", e);
|
||||
console.error('MQTT数据解析失败', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -241,14 +303,14 @@
|
||||
function createQrCanvas() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.id = 'remote-qr-canvas';
|
||||
canvas.className = "absolute inset-0 w-full h-full p-4 bg-white";
|
||||
canvas.className = 'absolute inset-0 w-full h-full p-4 bg-white';
|
||||
qrVideo.parentElement.appendChild(canvas);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
async function startLocalCamera(deviceId) {
|
||||
const constraints = {
|
||||
video: deviceId ? { deviceId: { exact: deviceId } } : { facingMode: 'environment' }
|
||||
video: deviceId ? { deviceId: { exact: deviceId } } : { facingMode: 'environment' },
|
||||
};
|
||||
try {
|
||||
videoStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
@ -265,7 +327,7 @@
|
||||
stopCamera();
|
||||
const mode = cameraSelect.value;
|
||||
|
||||
if (mode === "WEBSOCKET_MODE") {
|
||||
if (mode === 'WEBSOCKET_MODE') {
|
||||
startRemoteMode();
|
||||
} else {
|
||||
startLocalCamera(mode);
|
||||
@ -303,7 +365,7 @@
|
||||
lcscId: pcMatch[1].trim().toUpperCase(),
|
||||
name: pmMatch ? pmMatch[1].trim() : '未知型号',
|
||||
quantity: qtyMatch ? parseInt(qtyMatch[1]) : 1,
|
||||
selected: true
|
||||
selected: true,
|
||||
};
|
||||
|
||||
document.getElementById('res-cid').textContent = currentScanData.lcscId;
|
||||
@ -311,7 +373,7 @@
|
||||
document.getElementById('res-qty').textContent = currentScanData.quantity;
|
||||
document.getElementById('btn-add-to-list').disabled = false;
|
||||
} else {
|
||||
throw new Error("无效的二维码格式");
|
||||
throw new Error('无效的二维码格式');
|
||||
}
|
||||
} catch (e) {
|
||||
eda.sys_Message.showToastMessage('解析失败: ' + e.message, ESYS_ToastMessageType.ERROR);
|
||||
@ -344,13 +406,16 @@
|
||||
}
|
||||
};
|
||||
|
||||
window.updateQty = (index, val) => { importList[index].quantity = parseInt(val) || 0; };
|
||||
window.removeItem = (index) => { importList.splice(index, 1); renderList(); };
|
||||
window.updateQty = (index, val) => {
|
||||
importList[index].quantity = parseInt(val) || 0;
|
||||
};
|
||||
window.removeItem = (index) => {
|
||||
importList.splice(index, 1);
|
||||
renderList();
|
||||
};
|
||||
|
||||
document.getElementById('import-order-btn').onclick = async () => {
|
||||
|
||||
eda.sys_Dialog.showInformationMessage('此功能因可能存在的安全问题已被弃用', '提示', '知道了');
|
||||
|
||||
};
|
||||
|
||||
document.getElementById('open-qr-btn').onclick = async () => {
|
||||
@ -395,7 +460,7 @@
|
||||
quantity: quantity,
|
||||
value: '-',
|
||||
childCat: '-',
|
||||
selected: true
|
||||
selected: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -411,7 +476,6 @@
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
||||
} catch (err) {
|
||||
eda.sys_Message.showToastMessage('读取文件失败: ' + err.message, ESYS_ToastMessageType.ERROR);
|
||||
eda.sys_Log.add('Excel 导入错误: ' + err.stack);
|
||||
@ -437,7 +501,7 @@
|
||||
};
|
||||
|
||||
async function enrichData(items) {
|
||||
const lcscIds = items.map(i => i.lcscId);
|
||||
const lcscIds = items.map((i) => i.lcscId);
|
||||
const devs = await eda.lib_Device.getByLcscIds(lcscIds);
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
@ -446,12 +510,10 @@
|
||||
|
||||
if (dev && dev.uuid) {
|
||||
try {
|
||||
const infoRes = await eda.sys_ClientUrl.request(
|
||||
'https://client/api/v2/devices/' + dev.uuid,
|
||||
'GET',
|
||||
null,
|
||||
{ headers: { path: '0819f05c4eef4c71ace90d822a990e87' } }
|
||||
);
|
||||
const EDA_HOST = eda.sys_Environment.isClient() ? 'https://client' : 'https://pro.lceda.cn';
|
||||
const infoRes = await eda.sys_ClientUrl.request(EDA_HOST + '/api/v2/devices/' + dev.uuid, 'GET', null, {
|
||||
headers: { path: '0819f05c4eef4c71ace90d822a990e87' },
|
||||
});
|
||||
const infoJson = await infoRes.json();
|
||||
const info = infoJson.result;
|
||||
|
||||
@ -472,11 +534,11 @@
|
||||
}
|
||||
|
||||
document.getElementById('select-all').onclick = () => {
|
||||
importList.forEach(i => i.selected = true);
|
||||
importList.forEach((i) => (i.selected = true));
|
||||
renderList();
|
||||
};
|
||||
document.getElementById('select-reverse').onclick = () => {
|
||||
importList.forEach(i => i.selected = !i.selected);
|
||||
importList.forEach((i) => (i.selected = !i.selected));
|
||||
renderList();
|
||||
};
|
||||
tableBody.addEventListener('change', (e) => {
|
||||
@ -492,7 +554,7 @@
|
||||
};
|
||||
|
||||
document.getElementById('batch-save-btn').onclick = async () => {
|
||||
const toSave = importList.filter(i => i.selected && i.lcscId);
|
||||
const toSave = importList.filter((i) => i.selected && i.lcscId);
|
||||
|
||||
if (toSave.length === 0) {
|
||||
return eda.sys_Message.showToastMessage('请先选择要入库的项', ESYS_ToastMessageType.WARNING);
|
||||
@ -506,15 +568,12 @@
|
||||
const postData = JSON.stringify({
|
||||
name: item.name || '-',
|
||||
lcscId: item.lcscId,
|
||||
quantity: item.quantity
|
||||
quantity: item.quantity,
|
||||
});
|
||||
|
||||
let res = await eda.sys_ClientUrl.request(
|
||||
SERVER + '/addLeyeList',
|
||||
'POST',
|
||||
postData,
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
let res = await eda.sys_ClientUrl.request(SERVER + '/addLeyeList', 'POST', postData, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
let result = await res.json();
|
||||
|
||||
@ -522,14 +581,11 @@
|
||||
window.open('leye://open');
|
||||
for (let i = 0; i < 3 && !result.success; i++) {
|
||||
eda.sys_Message.showToastMessage('等待拉起本地服务端...', ESYS_ToastMessageType.INFO);
|
||||
res = await eda.sys_ClientUrl.request(
|
||||
SERVER + '/addLeyeList',
|
||||
'POST',
|
||||
postData,
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
res = await eda.sys_ClientUrl.request(SERVER + '/addLeyeList', 'POST', postData, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
result = await res.json();
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,11 +608,13 @@
|
||||
if (failCount === 0) {
|
||||
eda.sys_Message.showToastMessage(`全部入库成功,共 ${successCount} 项`, ESYS_ToastMessageType.SUCCESS);
|
||||
} else {
|
||||
eda.sys_Message.showToastMessage(`入库完成,成功: ${successCount}, 失败: ${failCount}`,
|
||||
failCount > 0 ? ESYS_ToastMessageType.WARNING : ESYS_ToastMessageType.SUCCESS);
|
||||
eda.sys_Message.showToastMessage(
|
||||
`入库完成,成功: ${successCount}, 失败: ${failCount}`,
|
||||
failCount > 0 ? ESYS_ToastMessageType.WARNING : ESYS_ToastMessageType.SUCCESS,
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,34 +1,78 @@
|
||||
<!doctype html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>查看库存 - LEYE</title>
|
||||
<link href="/iframe/css/index.css" rel="stylesheet" />
|
||||
<style>
|
||||
html { user-select: none; }
|
||||
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
|
||||
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
||||
#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 - 48px - 52px - 64px); overflow-y: auto; }
|
||||
.sticky-header { position: sticky; top: 0; z-index: 10; }
|
||||
.sort-icon { display: inline-block; margin-left: 4px; font-size: 10px; color: #9ca3af; }
|
||||
.sort-active { color: #2563eb !important; }
|
||||
.hidden { display: none !important; }
|
||||
.cat-toggle { transition: transform 0.2s; cursor: pointer; padding: 4px; }
|
||||
.cat-toggle.collapsed { transform: rotate(-90deg); }
|
||||
.children-container { overflow: hidden; transition: max-height 0.3s ease-out; }
|
||||
.children-container.hidden { display: none; }
|
||||
html {
|
||||
user-select: none;
|
||||
}
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
#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 - 48px - 52px - 64px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.sticky-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
.sort-icon {
|
||||
display: inline-block;
|
||||
margin-left: 4px;
|
||||
font-size: 10px;
|
||||
color: #9ca3af;
|
||||
}
|
||||
.sort-active {
|
||||
color: #2563eb !important;
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.cat-toggle {
|
||||
transition: transform 0.2s;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
}
|
||||
.cat-toggle.collapsed {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.children-container {
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out;
|
||||
}
|
||||
.children-container.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 font-sans text-sm">
|
||||
<div id="fixed-window" class="bg-gray-50 flex flex-col overflow-hidden">
|
||||
</head>
|
||||
<body class="bg-gray-100 font-sans text-sm">
|
||||
<div id="fixed-window" class="bg-gray-50 flex flex-col overflow-hidden">
|
||||
<header class="flex-shrink-0 bg-white border-b border-gray-200 shadow-sm p-2 text-sm">
|
||||
<div class="max-w-full mx-auto flex items-center space-x-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<input id="global-search-input" type="text" placeholder="搜索型号、CID、品牌、值..." class="w-[92%] px-2 py-1 text-sm border-0 focus:ring-0 focus:outline-none placeholder-gray-400" />
|
||||
<input
|
||||
id="global-search-input"
|
||||
type="text"
|
||||
placeholder="搜索型号、CID、品牌、值..."
|
||||
class="w-[92%] px-2 py-1 text-sm border-0 focus:ring-0 focus:outline-none placeholder-gray-400"
|
||||
/>
|
||||
<button id="search-btn" class="w-[5%] px-3 py-1 bg-blue-600 text-white rounded-md hover:bg-blue-700 text-xs">搜索</button>
|
||||
</div>
|
||||
</header>
|
||||
@ -36,8 +80,7 @@
|
||||
<div id="main-content" class="flex-grow flex p-3 space-x-3 overflow-hidden">
|
||||
<aside class="w-60 flex-shrink-0 bg-white border border-gray-200 rounded-lg shadow-md p-3 flex flex-col overflow-hidden">
|
||||
<h3 class="text-base font-semibold text-gray-800 border-b pb-1 mb-2">筛选类别</h3>
|
||||
<div id="category-tree" class="flex-grow overflow-y-auto scrollbar-hide space-y-0.5 text-xs">
|
||||
</div>
|
||||
<div id="category-tree" class="flex-grow overflow-y-auto scrollbar-hide space-y-0.5 text-xs"></div>
|
||||
</aside>
|
||||
|
||||
<main class="flex-grow flex flex-col bg-white border border-gray-200 rounded-lg shadow-md overflow-hidden">
|
||||
@ -47,16 +90,24 @@
|
||||
<tr>
|
||||
<th scope="col" class="w-12 px-2 py-2 text-center">ID</th>
|
||||
<th scope="col" class="w-48 px-3 py-2 text-left">型号</th>
|
||||
<th scope="col" id="sort-type" class="w-24 px-3 py-2 text-left cursor-pointer hover:bg-gray-200">类型 <span class="sort-icon">⇅</span></th>
|
||||
<th scope="col" id="sort-value" class="w-20 px-3 py-2 text-left cursor-pointer hover:bg-gray-200">值 <span class="sort-icon">⇅</span></th>
|
||||
<th scope="col" id="sort-type" class="w-24 px-3 py-2 text-left cursor-pointer hover:bg-gray-200">
|
||||
类型 <span class="sort-icon">⇅</span>
|
||||
</th>
|
||||
<th scope="col" id="sort-value" class="w-20 px-3 py-2 text-left cursor-pointer hover:bg-gray-200">
|
||||
值 <span class="sort-icon">⇅</span>
|
||||
</th>
|
||||
<th scope="col" class="w-24 px-3 py-2 text-left">封装</th>
|
||||
<th scope="col" class="w-32 px-3 py-2 text-left">品牌</th>
|
||||
<th scope="col" id="sort-quantity" class="w-24 px-3 py-2 text-right cursor-pointer hover:bg-gray-200">余量 <span class="sort-icon">⇅</span></th>
|
||||
<th scope="col" id="sort-quantity" class="w-24 px-3 py-2 text-right cursor-pointer hover:bg-gray-200">
|
||||
余量 <span class="sort-icon">⇅</span>
|
||||
</th>
|
||||
<th scope="col" class="w-32 px-3 py-2 text-left">CID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="data-table-body" class="bg-white divide-y divide-gray-200 text-xs">
|
||||
<tr><td colspan="8" class="text-center py-6 text-gray-500">正在初始化器件数据...</td></tr>
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-6 text-gray-500">正在初始化器件数据...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -65,9 +116,13 @@
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="text-gray-600">展示 <span id="display-range">0-0</span> / 共 <span id="total-items">0</span> 条</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<button id="prev-page" class="px-2 py-0.5 border border-gray-300 rounded-md hover:bg-gray-200 disabled:opacity-30">上页</button>
|
||||
<button id="prev-page" class="px-2 py-0.5 border border-gray-300 rounded-md hover:bg-gray-200 disabled:opacity-30">
|
||||
上页
|
||||
</button>
|
||||
<span class="px-2 font-medium" id="page-num">1</span>
|
||||
<button id="next-page" class="px-2 py-0.5 border border-gray-300 rounded-md hover:bg-gray-200 disabled:opacity-30">下页</button>
|
||||
<button id="next-page" class="px-2 py-0.5 border border-gray-300 rounded-md hover:bg-gray-200 disabled:opacity-30">
|
||||
下页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,39 +145,59 @@
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="col-span-2">
|
||||
<label class="block text-xs text-gray-500 mb-1">型号 (Manufacturer Part) <span class="text-red-500">*</span></label>
|
||||
<input id="edit-name-input" type="text" class="w-full px-2 py-1.5 border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none text-sm" placeholder="必填" />
|
||||
<input
|
||||
id="edit-name-input"
|
||||
type="text"
|
||||
class="w-full px-2 py-1.5 border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none text-sm"
|
||||
placeholder="必填"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label class="block text-xs text-gray-500 mb-1">LCSC ID (Supplier Part) <span class="text-red-500">*</span></label>
|
||||
<input id="edit-lcsc-input" type="text" class="w-full px-2 py-1.5 border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none text-sm" placeholder="必填" />
|
||||
<input
|
||||
id="edit-lcsc-input"
|
||||
type="text"
|
||||
class="w-full px-2 py-1.5 border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none text-sm"
|
||||
placeholder="必填"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="border-gray-100">
|
||||
<hr class="border-gray-100" />
|
||||
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">变更库存</label>
|
||||
<div class="flex space-x-2 mb-2">
|
||||
<button id="op-add" class="flex-1 py-1.5 border border-blue-600 bg-blue-50 text-blue-600 rounded text-xs font-medium">入库 (+)</button>
|
||||
<button id="op-sub" class="flex-1 py-1.5 border border-gray-300 text-gray-600 rounded text-xs font-medium">出库 (-)</button>
|
||||
<button id="op-add" class="flex-1 py-1.5 border border-blue-600 bg-blue-50 text-blue-600 rounded text-xs font-medium">
|
||||
入库 (+)
|
||||
</button>
|
||||
<button id="op-sub" class="flex-1 py-1.5 border border-gray-300 text-gray-600 rounded text-xs font-medium">
|
||||
出库 (-)
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<input id="edit-qty-input" type="number" min="0" value="0" class="w-24 px-2 py-1.5 border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none text-sm" />
|
||||
<div class="text-[10px] text-gray-400">
|
||||
<span id="current-qty-val">0</span> → <span id="target-qty-val">0</span>
|
||||
</div>
|
||||
<input
|
||||
id="edit-qty-input"
|
||||
type="number"
|
||||
min="0"
|
||||
value="0"
|
||||
class="w-24 px-2 py-1.5 border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none text-sm"
|
||||
/>
|
||||
<div class="text-[10px] text-gray-400"><span id="current-qty-val">0</span> → <span id="target-qty-val">0</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex border-t border-gray-100">
|
||||
<button id="dialog-cancel" class="flex-1 px-4 py-3 text-gray-500 hover:bg-gray-50 text-xs">取消</button>
|
||||
<button id="dialog-confirm" class="flex-1 px-4 py-3 bg-blue-600 text-white hover:bg-blue-700 text-xs font-bold">提交更改</button>
|
||||
<button id="dialog-confirm" class="flex-1 px-4 py-3 bg-blue-600 text-white hover:bg-blue-700 text-xs font-bold">
|
||||
提交更改
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const categoryTree = document.getElementById('category-tree');
|
||||
const tableBody = document.getElementById('data-table-body');
|
||||
@ -146,8 +221,8 @@
|
||||
const editLcscInput = document.getElementById('edit-lcsc-input');
|
||||
const dialogIdDisplay = document.getElementById('dialog-id-display');
|
||||
|
||||
const SERVER = eda.sys_Storage.getExtensionUserConfig('server-host') || 'http://localhost:21816/api';
|
||||
const AUTO_RUN = eda.sys_Storage.getExtensionUserConfig('server-auto-run') || true;
|
||||
const SERVER = eda.sys_Storage.getExtensionUserConfig('server-host') ?? 'http://localhost:21816/api';
|
||||
const AUTO_RUN = eda.sys_Storage.getExtensionUserConfig('server-auto-run') ?? true;
|
||||
const CACHE_KEY = 'cache-leye-device-details';
|
||||
|
||||
let allDevicesData = [];
|
||||
@ -155,13 +230,20 @@
|
||||
let selectedRowData = null;
|
||||
let sortRules = [
|
||||
{ key: 'type', order: 'desc' },
|
||||
{ key: 'value', order: 'desc' }
|
||||
{ key: 'value', order: 'desc' },
|
||||
];
|
||||
let currentOp = 'add';
|
||||
let currentPage = 1;
|
||||
const pageSize = 20;
|
||||
|
||||
const unitMap = new Map([['M', 1e6], ['k', 1e3], ['m', 1e-3], ['u', 1e-6], ['n', 1e-9], ['p', 1e-12]]);
|
||||
const unitMap = new Map([
|
||||
['M', 1e6],
|
||||
['k', 1e3],
|
||||
['m', 1e-3],
|
||||
['u', 1e-6],
|
||||
['n', 1e-9],
|
||||
['p', 1e-12],
|
||||
]);
|
||||
|
||||
function parseValue(val) {
|
||||
if (!val || val === '-') return -Infinity;
|
||||
@ -180,7 +262,11 @@
|
||||
try {
|
||||
const cachedRaw = await eda.sys_Storage.getExtensionUserConfig(CACHE_KEY);
|
||||
let cachedDetails = [];
|
||||
try { cachedDetails = cachedRaw ? JSON.parse(cachedRaw) : []; } catch(e) { cachedDetails = []; }
|
||||
try {
|
||||
cachedDetails = cachedRaw ? JSON.parse(cachedRaw) : [];
|
||||
} catch (e) {
|
||||
cachedDetails = [];
|
||||
}
|
||||
|
||||
let listRes = await eda.sys_ClientUrl.request(SERVER + '/getLeyeList?pageSize=1000¤t=1');
|
||||
let listResult = await listRes.json();
|
||||
@ -190,29 +276,32 @@
|
||||
eda.sys_Message.showToastMessage('等待拉起本地服务端...', ESYS_ToastMessageType.INFO);
|
||||
listRes = await eda.sys_ClientUrl.request(SERVER + '/getLeyeList?pageSize=1000¤t=1');
|
||||
listResult = await listRes.json();
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
}
|
||||
}
|
||||
if (!listResult.success || !listResult.data) {
|
||||
throw new Error('同步失败');
|
||||
}
|
||||
|
||||
const validItems = listResult.data.filter(item => item.lcscId);
|
||||
const currentLcscIds = validItems.map(item => String(item.lcscId).toUpperCase());
|
||||
const cachedLcscIds = cachedDetails.map(d => String(d.lcscId).toUpperCase());
|
||||
const newLcscIds = currentLcscIds.filter(id => !cachedLcscIds.includes(id));
|
||||
let updatedDetails = cachedDetails.filter(d => currentLcscIds.includes(String(d.lcscId).toUpperCase()));
|
||||
const validItems = listResult.data.filter((item) => item.lcscId);
|
||||
const currentLcscIds = validItems.map((item) => String(item.lcscId).toUpperCase());
|
||||
const cachedLcscIds = cachedDetails.map((d) => String(d.lcscId).toUpperCase());
|
||||
const newLcscIds = currentLcscIds.filter((id) => !cachedLcscIds.includes(id));
|
||||
let updatedDetails = cachedDetails.filter((d) => currentLcscIds.includes(String(d.lcscId).toUpperCase()));
|
||||
|
||||
if (newLcscIds.length > 0) {
|
||||
const devices = await eda.lib_Device.getByLcscIds(newLcscIds);
|
||||
const chunkSize = 20;
|
||||
for (let i = 0; i < devices.length; i += chunkSize) {
|
||||
const chunk = devices.slice(i, i + chunkSize);
|
||||
tableBody.innerHTML = `<tr><td colspan="8" class="text-center py-6 text-gray-500">发现 ${newLcscIds.length} 个新器件,正在更新 (${Math.min(i+chunkSize, devices.length)}/${devices.length})...</td></tr>`;
|
||||
const chunkData = await Promise.all(chunk.map(async (dev) => {
|
||||
tableBody.innerHTML = `<tr><td colspan="8" class="text-center py-6 text-gray-500">发现 ${newLcscIds.length} 个新器件,正在更新 (${Math.min(i + chunkSize, devices.length)}/${devices.length})...</td></tr>`;
|
||||
const chunkData = await Promise.all(
|
||||
chunk.map(async (dev) => {
|
||||
try {
|
||||
const EDA_HOST = eda.sys_Environment.isClient() ? "https://client" : "https://pro.lceda.cn";
|
||||
const infoRes = await eda.sys_ClientUrl.request(EDA_HOST + '/api/v2/devices/' + dev.uuid, 'GET', null, { headers: { path: '0819f05c4eef4c71ace90d822a990e87' } });
|
||||
const EDA_HOST = eda.sys_Environment.isClient() ? 'https://client' : 'https://pro.lceda.cn';
|
||||
const infoRes = await eda.sys_ClientUrl.request(EDA_HOST + '/api/v2/devices/' + dev.uuid, 'GET', null, {
|
||||
headers: { path: '0819f05c4eef4c71ace90d822a990e87' },
|
||||
});
|
||||
const infoJson = await infoRes.json();
|
||||
const info = infoJson.result;
|
||||
return {
|
||||
@ -225,15 +314,18 @@
|
||||
brand: info.attributes['Manufacturer'] || '-',
|
||||
lcscId: String(info.attributes['Supplier Part']).toUpperCase(),
|
||||
};
|
||||
} catch (e) { return null; }
|
||||
}));
|
||||
updatedDetails.push(...chunkData.filter(d => d !== null));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
);
|
||||
updatedDetails.push(...chunkData.filter((d) => d !== null));
|
||||
}
|
||||
await eda.sys_Storage.setExtensionUserConfig(CACHE_KEY, JSON.stringify(updatedDetails));
|
||||
}
|
||||
|
||||
allDevicesData = updatedDetails.map(detail => {
|
||||
const invItem = validItems.find(vi => String(vi.lcscId).toUpperCase() === detail.lcscId);
|
||||
allDevicesData = updatedDetails.map((detail) => {
|
||||
const invItem = validItems.find((vi) => String(vi.lcscId).toUpperCase() === detail.lcscId);
|
||||
return { ...detail, id: invItem ? invItem.id : '?', quantity: invItem ? invItem.quantity : 0 };
|
||||
});
|
||||
|
||||
@ -248,7 +340,7 @@
|
||||
|
||||
function generateCategoryTree() {
|
||||
const tree = {};
|
||||
allDevicesData.forEach(d => {
|
||||
allDevicesData.forEach((d) => {
|
||||
if (!tree[d.parentCat]) tree[d.parentCat] = new Set();
|
||||
tree[d.parentCat].add(d.childCat);
|
||||
});
|
||||
@ -260,7 +352,9 @@
|
||||
<label for="cat-all" class="font-bold text-gray-900 cursor-pointer">全部</label>
|
||||
</div>`;
|
||||
|
||||
Object.keys(tree).sort().forEach(parent => {
|
||||
Object.keys(tree)
|
||||
.sort()
|
||||
.forEach((parent) => {
|
||||
const parentId = btoa(encodeURIComponent(parent)).replace(/=/g, '');
|
||||
html += `
|
||||
<div class="parent-group">
|
||||
@ -271,7 +365,9 @@
|
||||
</div>
|
||||
<div id="children-${parentId}" class="children-container ml-4 border-l border-gray-100 pl-2 hidden">`;
|
||||
|
||||
Array.from(tree[parent]).sort().forEach(child => {
|
||||
Array.from(tree[parent])
|
||||
.sort()
|
||||
.forEach((child) => {
|
||||
const childId = btoa(encodeURIComponent(child)).replace(/=/g, '');
|
||||
html += `
|
||||
<div class="cursor-pointer hover:bg-blue-50 rounded p-1 flex items-center" data-cat="${child}">
|
||||
@ -284,7 +380,7 @@
|
||||
});
|
||||
categoryTree.innerHTML = html;
|
||||
|
||||
categoryTree.querySelectorAll('.cat-toggle').forEach(btn => {
|
||||
categoryTree.querySelectorAll('.cat-toggle').forEach((btn) => {
|
||||
btn.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
const targetId = btn.dataset.toggle;
|
||||
@ -301,8 +397,9 @@
|
||||
const selectedRadio = document.querySelector('input[name="category"]:checked');
|
||||
const catValue = selectedRadio ? selectedRadio.value : 'all';
|
||||
|
||||
filteredData = allDevicesData.filter(d => {
|
||||
const matchesSearch = !keyword ||
|
||||
filteredData = allDevicesData.filter((d) => {
|
||||
const matchesSearch =
|
||||
!keyword ||
|
||||
(d.name && d.name.toLowerCase().includes(keyword)) ||
|
||||
(d.lcscId && d.lcscId.toLowerCase().includes(keyword)) ||
|
||||
(d.brand && d.brand.toLowerCase().includes(keyword)) ||
|
||||
@ -319,9 +416,16 @@
|
||||
data.sort((a, b) => {
|
||||
for (const rule of sortRules) {
|
||||
let valA, valB;
|
||||
if (rule.key === 'type') { valA = a.childCat; valB = b.childCat; }
|
||||
else if (rule.key === 'value') { valA = parseValue(a.value); valB = parseValue(b.value); }
|
||||
else if (rule.key === 'quantity') { valA = a.quantity; valB = b.quantity; }
|
||||
if (rule.key === 'type') {
|
||||
valA = a.childCat;
|
||||
valB = b.childCat;
|
||||
} else if (rule.key === 'value') {
|
||||
valA = parseValue(a.value);
|
||||
valB = parseValue(b.value);
|
||||
} else if (rule.key === 'quantity') {
|
||||
valA = a.quantity;
|
||||
valB = b.quantity;
|
||||
}
|
||||
|
||||
if (valA === valB) continue;
|
||||
if (typeof valA === 'number' && typeof valB === 'number') {
|
||||
@ -353,7 +457,9 @@
|
||||
document.getElementById('prev-page').disabled = currentPage === 1;
|
||||
document.getElementById('next-page').disabled = end >= total;
|
||||
|
||||
tableBody.innerHTML = pageData.map(item => `
|
||||
tableBody.innerHTML = pageData
|
||||
.map(
|
||||
(item) => `
|
||||
<tr class="hover:bg-blue-50 cursor-pointer ${selectedRowData && selectedRowData.lcscId === item.lcscId ? 'bg-blue-100' : ''}" data-row='${JSON.stringify(item)}'>
|
||||
<td class="px-2 py-1.5 text-center text-gray-500">${item.id}</td>
|
||||
<td class="px-3 py-1.5 font-medium text-blue-600">${item.name}</td>
|
||||
@ -364,21 +470,23 @@
|
||||
<td class="px-3 py-1.5 text-right ${item.quantity > 0 ? 'text-green-600' : 'text-red-500'}">${item.quantity}</td>
|
||||
<td class="px-3 py-1.5 text-left text-gray-500">${item.lcscId}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
`,
|
||||
)
|
||||
.join('');
|
||||
}
|
||||
|
||||
function selectRow(row) {
|
||||
tableBody.querySelectorAll('tr').forEach(r => r.classList.remove('bg-blue-100'));
|
||||
tableBody.querySelectorAll('tr').forEach((r) => r.classList.remove('bg-blue-100'));
|
||||
row.classList.add('bg-blue-100');
|
||||
selectedRowData = JSON.parse(row.dataset.row);
|
||||
selectedRowsInfo.textContent = '已选择 1 行';
|
||||
}
|
||||
|
||||
function updateSortIcons() {
|
||||
['type', 'value', 'quantity'].forEach(key => {
|
||||
['type', 'value', 'quantity'].forEach((key) => {
|
||||
const th = document.getElementById(`sort-${key}`);
|
||||
const icon = th.querySelector('.sort-icon');
|
||||
const ruleIndex = sortRules.findIndex(r => r.key === key);
|
||||
const ruleIndex = sortRules.findIndex((r) => r.key === key);
|
||||
const rule = sortRules[ruleIndex];
|
||||
if (rule) {
|
||||
icon.textContent = (rule.order === 'asc' ? '↑' : '↓') + (sortRules.length > 1 ? `(${ruleIndex + 1})` : '');
|
||||
@ -394,8 +502,18 @@
|
||||
|
||||
searchButton.onclick = handleLocalSearch;
|
||||
searchInput.onkeyup = (e) => e.key === 'Enter' && handleLocalSearch();
|
||||
document.getElementById('prev-page').onclick = () => { if (currentPage > 1) { currentPage--; applySortAndRender(); } };
|
||||
document.getElementById('next-page').onclick = () => { if (currentPage * pageSize < filteredData.length) { currentPage++; applySortAndRender(); } };
|
||||
document.getElementById('prev-page').onclick = () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
applySortAndRender();
|
||||
}
|
||||
};
|
||||
document.getElementById('next-page').onclick = () => {
|
||||
if (currentPage * pageSize < filteredData.length) {
|
||||
currentPage++;
|
||||
applySortAndRender();
|
||||
}
|
||||
};
|
||||
|
||||
categoryTree.onclick = (e) => {
|
||||
const div = e.target.closest('div[data-cat]');
|
||||
@ -406,7 +524,7 @@
|
||||
};
|
||||
|
||||
const onSortClick = (key) => {
|
||||
const idx = sortRules.findIndex(r => r.key === key);
|
||||
const idx = sortRules.findIndex((r) => r.key === key);
|
||||
if (idx > -1) {
|
||||
if (sortRules[idx].order === 'asc') sortRules[idx].order = 'desc';
|
||||
else sortRules.splice(idx, 1);
|
||||
@ -441,7 +559,9 @@
|
||||
uuid: selectedRowData.uuid,
|
||||
libraryUuid: '0819f05c4eef4c71ace90d822a990e87',
|
||||
});
|
||||
} catch (e) { console.error(e); }
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
editBtn.onclick = () => {
|
||||
@ -505,27 +625,30 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const newQuantity = currentOp === 'add' ?
|
||||
selectedRowData.quantity + changeQty :
|
||||
selectedRowData.quantity - changeQty;
|
||||
const newQuantity = currentOp === 'add' ? selectedRowData.quantity + changeQty : selectedRowData.quantity - changeQty;
|
||||
|
||||
dialogConfirm.disabled = true;
|
||||
dialogConfirm.textContent = '正在保存...';
|
||||
|
||||
try {
|
||||
const res = await eda.sys_ClientUrl.request(SERVER + '/editLeyeList', 'POST', JSON.stringify({
|
||||
const res = await eda.sys_ClientUrl.request(
|
||||
SERVER + '/editLeyeList',
|
||||
'POST',
|
||||
JSON.stringify({
|
||||
id: selectedRowData.id,
|
||||
name: newName,
|
||||
lcscId: newLcscId,
|
||||
quantity: newQuantity
|
||||
}), { headers: { 'Content-Type': 'application/json' } });
|
||||
quantity: newQuantity,
|
||||
}),
|
||||
{ headers: { 'Content-Type': 'application/json' } },
|
||||
);
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
if (result.success) {
|
||||
eda.sys_Message.showToastMessage('更新成功', ESYS_ToastMessageType.SUCCESS);
|
||||
|
||||
const deviceIndex = allDevicesData.findIndex(d => d.id === selectedRowData.id);
|
||||
const deviceIndex = allDevicesData.findIndex((d) => d.id === selectedRowData.id);
|
||||
if (deviceIndex !== -1) {
|
||||
allDevicesData[deviceIndex].name = newName;
|
||||
allDevicesData[deviceIndex].lcscId = newLcscId;
|
||||
@ -552,6 +675,6 @@
|
||||
|
||||
initInventoryData();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user