feat: 添加 AI 重复物料巡检功能,支持生成人工复核清单和导出 CSV

This commit is contained in:
2026-03-12 15:08:52 +08:00
parent ef0af75193
commit 168f5fe49c
4 changed files with 518 additions and 5 deletions

View File

@@ -101,6 +101,21 @@
<summary>查看原始 AI 文本</summary>
<pre id="ai-panel-content" class="ai-panel-content"></pre>
</details>
<hr class="ai-divider">
<div class="ai-panel-head">
<h2>AI重复物料巡检</h2>
</div>
<p class="hint">扫描疑似同料号、同参数、同立创编号记录,生成人工复核清单。</p>
<div class="actions">
<button class="btn" id="ai-duplicate-btn" type="button">开始巡检</button>
<button class="btn btn-light" id="ai-duplicate-export-current-btn" type="button">导出当前显示</button>
<button class="btn btn-light" id="ai-duplicate-export-all-btn" type="button">导出全量</button>
</div>
<p class="hint" id="ai-duplicate-status"></p>
<p class="hint" id="ai-duplicate-warning"></p>
<div id="ai-duplicate-groups" class="ai-plan-groups"></div>
</section>
</aside>
</div>
@@ -244,6 +259,167 @@
});
});
})();
(function () {
var auditBtn = document.getElementById('ai-duplicate-btn');
var exportCurrentBtn = document.getElementById('ai-duplicate-export-current-btn');
var exportAllBtn = document.getElementById('ai-duplicate-export-all-btn');
var statusNode = document.getElementById('ai-duplicate-status');
var warningNode = document.getElementById('ai-duplicate-warning');
var groupsWrap = document.getElementById('ai-duplicate-groups');
var latestAuditGroups = [];
if (!auditBtn || !exportCurrentBtn || !exportAllBtn || !statusNode || !groupsWrap) {
return;
}
function clearGroups() {
latestAuditGroups = [];
groupsWrap.innerHTML = '';
}
function renderAuditGroups(groups) {
clearGroups();
if (!Array.isArray(groups) || !groups.length) {
var empty = document.createElement('p');
empty.className = 'muted';
empty.textContent = '未发现疑似重复物料';
groupsWrap.appendChild(empty);
return;
}
latestAuditGroups = groups.slice();
groups.forEach(function (group) {
var section = document.createElement('section');
section.className = 'ai-plan-group ai-audit-group';
var title = document.createElement('h3');
title.textContent = (group.reason || '疑似重复') + ' | ' + (group.key || '-') + '' + (group.member_count || 0) + '';
section.appendChild(title);
var suggestion = document.createElement('p');
suggestion.className = 'hint';
suggestion.textContent = '建议: ' + (group.suggestion || '请人工复核');
section.appendChild(suggestion);
var list = document.createElement('ul');
list.className = 'side-low-stock-list';
(group.members || []).forEach(function (member) {
var li = document.createElement('li');
var content = document.createElement('div');
var strong = document.createElement('strong');
strong.textContent = (member.name || '未命名元件') + ' (' + (member.part_no || '-') + ')';
content.appendChild(strong);
var spec = document.createElement('p');
spec.className = 'hint';
spec.textContent = '规格: ' + (member.specification || '-');
content.appendChild(spec);
var pos = document.createElement('p');
pos.className = 'hint';
pos.textContent = (member.box_name || '-') + ' / ' + (member.slot_code || '-') + ' | 数量 ' + (member.quantity || 0);
content.appendChild(pos);
if (member.lcsc_code) {
var lcsc = document.createElement('p');
lcsc.className = 'hint';
lcsc.textContent = '立创编号: ' + member.lcsc_code;
content.appendChild(lcsc);
}
li.appendChild(content);
if (member.edit_url) {
var editBtn = document.createElement('a');
editBtn.className = 'btn btn-light';
editBtn.href = member.edit_url;
editBtn.textContent = '编辑';
li.appendChild(editBtn);
}
list.appendChild(li);
});
section.appendChild(list);
groupsWrap.appendChild(section);
});
}
auditBtn.addEventListener('click', function () {
auditBtn.disabled = true;
statusNode.textContent = '正在巡检重复物料,请稍候...';
if (warningNode) {
warningNode.textContent = '';
}
clearGroups();
fetch('{{ url_for('ai_duplicate_audit') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: '{}'
})
.then(function (resp) {
return resp.json().then(function (data) {
return { ok: resp.ok, data: data };
});
})
.then(function (result) {
var data = result.data || {};
if (!result.ok || !data.ok) {
statusNode.textContent = '巡检失败';
if (warningNode) {
warningNode.textContent = data.message || '服务暂时不可用';
}
return;
}
statusNode.textContent = data.summary || '巡检已完成';
if (warningNode) {
warningNode.textContent = data.parse_warning || '';
}
renderAuditGroups((data.data && data.data.groups) || []);
})
.catch(function () {
statusNode.textContent = '巡检失败';
if (warningNode) {
warningNode.textContent = '请求失败,请稍后重试';
}
})
.finally(function () {
auditBtn.disabled = false;
});
});
exportCurrentBtn.addEventListener('click', function () {
if (!latestAuditGroups.length) {
statusNode.textContent = '请先运行巡检,再导出当前显示结果';
if (warningNode) {
warningNode.textContent = '';
}
return;
}
var params = new URLSearchParams();
params.set('limit', String(latestAuditGroups.length));
latestAuditGroups.forEach(function (group) {
params.append('group_id', (group.type || '') + '::' + (group.key || ''));
});
var downloadUrl = '{{ url_for('export_duplicate_audit_csv') }}?' + params.toString();
window.location.href = downloadUrl;
});
exportAllBtn.addEventListener('click', function () {
var downloadUrl = '{{ url_for('export_duplicate_audit_csv') }}?limit=1000';
window.location.href = downloadUrl;
});
})();
</script>
</body>
</html>