feat: 添加 AI 补货建议功能,优化相关设置和界面
This commit is contained in:
@@ -27,6 +27,9 @@
|
||||
<p class="notice">{{ notice }}</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="overview-layout">
|
||||
<section class="overview-main">
|
||||
|
||||
<section class="metrics-grid">
|
||||
<article class="metric-card">
|
||||
<p class="metric-title">容器总数</p>
|
||||
@@ -80,6 +83,167 @@
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<aside class="overview-sidebar">
|
||||
<section class="panel ai-panel" id="ai-panel">
|
||||
<div class="ai-panel-head">
|
||||
<h2>AI补货建议</h2>
|
||||
<a class="btn btn-light" href="{{ url_for('ai_settings_page') }}">参数</a>
|
||||
</div>
|
||||
<p class="hint">根据低库存与近30天出库数据,生成可执行补货建议。</p>
|
||||
<button class="btn" id="ai-restock-btn" type="button">生成建议</button>
|
||||
<p class="hint" id="ai-panel-status"></p>
|
||||
<p class="hint" id="ai-panel-warning"></p>
|
||||
<div id="ai-plan-groups" class="ai-plan-groups"></div>
|
||||
<details class="box-overview" id="ai-raw-wrap" hidden>
|
||||
<summary>查看原始 AI 文本</summary>
|
||||
<pre id="ai-panel-content" class="ai-panel-content"></pre>
|
||||
</details>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var aiBtn = document.getElementById('ai-restock-btn');
|
||||
var statusNode = document.getElementById('ai-panel-status');
|
||||
var warningNode = document.getElementById('ai-panel-warning');
|
||||
var contentNode = document.getElementById('ai-panel-content');
|
||||
var planGroups = document.getElementById('ai-plan-groups');
|
||||
var rawWrap = document.getElementById('ai-raw-wrap');
|
||||
|
||||
function clearPlan() {
|
||||
if (planGroups) {
|
||||
planGroups.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
function renderPlan(plan) {
|
||||
if (!planGroups) {
|
||||
return;
|
||||
}
|
||||
clearPlan();
|
||||
|
||||
var groups = [
|
||||
{ key: 'urgent', title: '紧急补货' },
|
||||
{ key: 'this_week', title: '本周建议补货' },
|
||||
{ key: 'defer', title: '暂缓补货' }
|
||||
];
|
||||
|
||||
groups.forEach(function (groupMeta) {
|
||||
var rows = (plan && plan[groupMeta.key]) || [];
|
||||
var wrap = document.createElement('section');
|
||||
wrap.className = 'ai-plan-group';
|
||||
|
||||
var title = document.createElement('h3');
|
||||
title.textContent = groupMeta.title + '(' + rows.length + ')';
|
||||
wrap.appendChild(title);
|
||||
|
||||
var list = document.createElement('ul');
|
||||
list.className = 'side-low-stock-list';
|
||||
if (!rows.length) {
|
||||
var empty = document.createElement('li');
|
||||
empty.className = 'muted';
|
||||
empty.textContent = '暂无条目';
|
||||
list.appendChild(empty);
|
||||
} else {
|
||||
rows.forEach(function (item) {
|
||||
var li = document.createElement('li');
|
||||
var content = document.createElement('div');
|
||||
|
||||
var strong = document.createElement('strong');
|
||||
strong.textContent = (item.name || '未命名元件') + ' (' + (item.part_no || '-') + ')';
|
||||
content.appendChild(strong);
|
||||
|
||||
var qty = document.createElement('p');
|
||||
qty.className = 'hint';
|
||||
qty.textContent = '建议补货: ' + (item.suggest_qty || '待确认');
|
||||
content.appendChild(qty);
|
||||
|
||||
var reason = document.createElement('p');
|
||||
reason.className = 'hint';
|
||||
reason.textContent = '理由: ' + (item.reason || '无');
|
||||
content.appendChild(reason);
|
||||
|
||||
li.appendChild(content);
|
||||
list.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
wrap.appendChild(list);
|
||||
planGroups.appendChild(wrap);
|
||||
});
|
||||
}
|
||||
|
||||
if (!aiBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
aiBtn.addEventListener('click', function () {
|
||||
aiBtn.disabled = true;
|
||||
statusNode.textContent = '正在生成建议,请稍候...';
|
||||
if (warningNode) {
|
||||
warningNode.textContent = '';
|
||||
}
|
||||
clearPlan();
|
||||
if (contentNode) {
|
||||
contentNode.textContent = '';
|
||||
}
|
||||
if (rawWrap) {
|
||||
rawWrap.hidden = true;
|
||||
}
|
||||
|
||||
fetch('{{ url_for('ai_restock_plan') }}', {
|
||||
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 || 'AI服务暂时不可用';
|
||||
}
|
||||
if (data.plan) {
|
||||
renderPlan(data.plan);
|
||||
}
|
||||
return;
|
||||
}
|
||||
statusNode.textContent = (data.plan && data.plan.summary) || '建议已生成';
|
||||
if (warningNode) {
|
||||
warningNode.textContent = data.parse_warning || '';
|
||||
}
|
||||
renderPlan(data.plan || {});
|
||||
if (contentNode && data.suggestion) {
|
||||
contentNode.textContent = data.suggestion;
|
||||
if (rawWrap) {
|
||||
rawWrap.hidden = false;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
statusNode.textContent = '生成失败';
|
||||
if (warningNode) {
|
||||
warningNode.textContent = '请求失败,请稍后重试';
|
||||
}
|
||||
})
|
||||
.finally(function () {
|
||||
aiBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user