320 lines
12 KiB
HTML
320 lines
12 KiB
HTML
|
|
<!doctype html>
|
||
|
|
<html lang="zh">
|
||
|
|
<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; }
|
||
|
|
</style>
|
||
|
|
<script src="/iframe/js/xlsx.full.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">
|
||
|
|
<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" />
|
||
|
|
<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>
|
||
|
|
</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" />
|
||
|
|
<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>
|
||
|
|
|
||
|
|
<div class="px-4 py-2 bg-gray-50 border-b flex justify-between items-center">
|
||
|
|
<div class="space-x-2">
|
||
|
|
<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>
|
||
|
|
|
||
|
|
<main class="flex-grow bg-white overflow-hidden m-3 border rounded-lg shadow-inner">
|
||
|
|
<div class="table-container overflow-x-auto">
|
||
|
|
<table class="min-w-full divide-y divide-gray-200">
|
||
|
|
<thead class="bg-gray-100 sticky-header text-xs uppercase text-gray-600">
|
||
|
|
<tr>
|
||
|
|
<th class="w-12 px-4 py-3 text-center">选择</th>
|
||
|
|
<th class="px-3 py-3 text-left">型号</th>
|
||
|
|
<th class="px-3 py-3 text-left">类型</th>
|
||
|
|
<th class="px-3 py-3 text-left">值</th>
|
||
|
|
<th class="px-3 py-3 text-left">封装</th>
|
||
|
|
<th class="px-3 py-3 text-left">品牌</th>
|
||
|
|
<th class="w-24 px-3 py-3 text-center">入库数量</th>
|
||
|
|
<th class="px-3 py-3 text-left">CID</th>
|
||
|
|
<th class="w-16 px-3 py-3 text-center">操作</th>
|
||
|
|
</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>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</main>
|
||
|
|
|
||
|
|
<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>
|
||
|
|
</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');
|
||
|
|
let importList = [];
|
||
|
|
|
||
|
|
function renderList() {
|
||
|
|
if (importList.length === 0) {
|
||
|
|
tableBody.innerHTML = `<tr id="empty-row"><td colspan="9" class="text-center py-20 text-gray-400">列表为空,请从上方导入数据</td></tr>`;
|
||
|
|
listCount.textContent = 0;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
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>
|
||
|
|
<td class="px-3 py-2 text-gray-600">${item.childCat || '-'}</td>
|
||
|
|
<td class="px-3 py-2">${item.value || '-'}</td>
|
||
|
|
<td class="px-3 py-2">${item.footprint || '-'}</td>
|
||
|
|
<td class="px-3 py-2">${item.brand || '-'}</td>
|
||
|
|
<td class="px-3 py-2 text-center">
|
||
|
|
<input type="number" class="w-16 border rounded text-center" value="${item.quantity}" onchange="updateQty(${index}, this.value)">
|
||
|
|
</td>
|
||
|
|
<td class="px-3 py-2 text-blue-600 font-mono">${item.lcscId}</td>
|
||
|
|
<td class="px-3 py-2 text-center">
|
||
|
|
<button onclick="removeItem(${index})" class="text-red-500 hover:text-red-700">删除</button>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
`).join('');
|
||
|
|
listCount.textContent = importList.length;
|
||
|
|
}
|
||
|
|
|
||
|
|
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('import-order-file-btn').onclick = async () => {
|
||
|
|
try {
|
||
|
|
const file = await eda.sys_FileSystem.openReadFileDialog(['xls', ['xlsx']], false);
|
||
|
|
|
||
|
|
if (!file) return;
|
||
|
|
|
||
|
|
const reader = new FileReader();
|
||
|
|
reader.onload = async (e) => {
|
||
|
|
const data = new Uint8Array(e.target.result);
|
||
|
|
const workbook = XLSX.read(data, { type: 'array' });
|
||
|
|
|
||
|
|
const firstSheetName = workbook.SheetNames[0];
|
||
|
|
const worksheet = workbook.Sheets[firstSheetName];
|
||
|
|
|
||
|
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||
|
|
|
||
|
|
const newItems = [];
|
||
|
|
for (let i = 18; i < jsonData.length; i++) {
|
||
|
|
const row = jsonData[i];
|
||
|
|
if (!row || row.length < 2) continue; // 跳过空行
|
||
|
|
|
||
|
|
// 第2列(1): lcscId, 第3列(2): 品牌, 第4列(3): 型号, 第5列(4): 封装, 第7列(6): 数量
|
||
|
|
const lcscId = (row[1] || '').toString().trim().toUpperCase();
|
||
|
|
if (!lcscId) continue;
|
||
|
|
|
||
|
|
const rawQty = (row[6] || '0').toString();
|
||
|
|
const qtyMatch = rawQty.match(/\d+/);
|
||
|
|
const quantity = qtyMatch ? parseInt(qtyMatch[0]) : 0;
|
||
|
|
|
||
|
|
newItems.push({
|
||
|
|
lcscId: lcscId,
|
||
|
|
brand: row[2] || '-',
|
||
|
|
name: row[3] || '-',
|
||
|
|
footprint: row[4] || '-',
|
||
|
|
quantity: quantity,
|
||
|
|
value: '-',
|
||
|
|
childCat: '-',
|
||
|
|
selected: true
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if (newItems.length > 0) {
|
||
|
|
// await enrichData(newItems);
|
||
|
|
|
||
|
|
importList.push(...newItems);
|
||
|
|
renderList();
|
||
|
|
eda.sys_Message.showToastMessage(`从文件成功导入 ${newItems.length} 个器件`, ESYS_ToastMessageType.SUCCESS);
|
||
|
|
} else {
|
||
|
|
eda.sys_Message.showToastMessage('未在找到有效数据', ESYS_ToastMessageType.WARNING);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
reader.readAsArrayBuffer(file);
|
||
|
|
|
||
|
|
} catch (err) {
|
||
|
|
eda.sys_Message.showToastMessage('读取文件失败: ' + err.message, ESYS_ToastMessageType.ERROR);
|
||
|
|
eda.sys_Log.add('Excel 导入错误: ' + err.stack);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
document.getElementById('import-cid-btn').onclick = async () => {
|
||
|
|
const cid = document.getElementById('single-cid').value.trim().toUpperCase();
|
||
|
|
const qty = parseInt(document.getElementById('single-qty').value);
|
||
|
|
if (!cid || !qty) return eda.sys_Message.showToastMessage('请完整填写编号和数量', ESYS_ToastMessageType.ERROR);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const items = [{ lcscId: cid, quantity: qty, selected: true }];
|
||
|
|
await enrichData(items);
|
||
|
|
importList.push(...items);
|
||
|
|
renderList();
|
||
|
|
document.getElementById('single-cid').value = '';
|
||
|
|
document.getElementById('single-qty').value = '';
|
||
|
|
} catch (e) {
|
||
|
|
eda.sys_Message.showToastMessage('查询失败', ESYS_ToastMessageType.ERROR);
|
||
|
|
eda.sys_Log.add(e.message);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
async function enrichData(items) {
|
||
|
|
const lcscIds = items.map(i => i.lcscId);
|
||
|
|
const devs = await eda.lib_Device.getByLcscIds(lcscIds);
|
||
|
|
|
||
|
|
for (let i = 0; i < items.length; i++) {
|
||
|
|
const dev = devs[i];
|
||
|
|
const item = items[i];
|
||
|
|
|
||
|
|
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 infoJson = await infoRes.json();
|
||
|
|
const info = infoJson.result;
|
||
|
|
|
||
|
|
if (info && info.attributes) {
|
||
|
|
item.name = info.attributes['Manufacturer Part'] || item.name;
|
||
|
|
item.childCat = info.tags?.child_tag?.name_cn || item.childCat;
|
||
|
|
item.value = info.attributes['Value'] || '-';
|
||
|
|
item.footprint = info.attributes['Supplier Footprint'] || item.footprint;
|
||
|
|
item.brand = info.attributes['Manufacturer'] || item.brand;
|
||
|
|
item.uuid = dev.uuid;
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.error(`获取器件 ${item.lcscId} 详情失败:`, e);
|
||
|
|
eda.sys_Log.add(`获取器件 ${item.lcscId} 详情失败: ${e.message}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
document.getElementById('select-all').onclick = () => {
|
||
|
|
importList.forEach(i => i.selected = true);
|
||
|
|
renderList();
|
||
|
|
};
|
||
|
|
document.getElementById('select-reverse').onclick = () => {
|
||
|
|
importList.forEach(i => i.selected = !i.selected);
|
||
|
|
renderList();
|
||
|
|
};
|
||
|
|
tableBody.addEventListener('change', (e) => {
|
||
|
|
if (e.target.classList.contains('row-checkbox')) {
|
||
|
|
const idx = e.target.dataset.index;
|
||
|
|
importList[idx].selected = e.target.checked;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById('clear-list-btn').onclick = () => {
|
||
|
|
importList = [];
|
||
|
|
renderList();
|
||
|
|
};
|
||
|
|
|
||
|
|
document.getElementById('batch-save-btn').onclick = async () => {
|
||
|
|
const toSave = importList.filter(i => i.selected && i.lcscId);
|
||
|
|
|
||
|
|
if (toSave.length === 0) {
|
||
|
|
return eda.sys_Message.showToastMessage('请先选择要入库的项', ESYS_ToastMessageType.WARNING);
|
||
|
|
}
|
||
|
|
|
||
|
|
let successCount = 0;
|
||
|
|
let failCount = 0;
|
||
|
|
const SERVER = await eda.sys_Storage.getExtensionUserConfig('server-host') || 'http://localhost:21816';
|
||
|
|
|
||
|
|
for (const item of toSave) {
|
||
|
|
try {
|
||
|
|
const postData = JSON.stringify({
|
||
|
|
name: item.name || '-',
|
||
|
|
lcscId: item.lcscId,
|
||
|
|
quantity: item.quantity
|
||
|
|
});
|
||
|
|
|
||
|
|
let res = await eda.sys_ClientUrl.request(
|
||
|
|
SERVER + '/addLeyeList',
|
||
|
|
'POST',
|
||
|
|
postData,
|
||
|
|
{ headers: { 'Content-Type': 'application/json' } }
|
||
|
|
);
|
||
|
|
|
||
|
|
let result = await res.json();
|
||
|
|
|
||
|
|
if (AUTO_RUN && !result.success) {
|
||
|
|
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' } }
|
||
|
|
);
|
||
|
|
result = await res.json();
|
||
|
|
await new Promise(resolve => setTimeout(resolve, 1500));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
successCount++;
|
||
|
|
const idx = importList.indexOf(item);
|
||
|
|
if (idx > -1) importList.splice(idx, 1);
|
||
|
|
} else {
|
||
|
|
failCount++;
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.error(`入库失败: ${item.lcscId}`, e);
|
||
|
|
eda.sys_Log.add(`入库失败: ${item.lcscId}: ${e.message}`);
|
||
|
|
failCount++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
renderList();
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|