351 lines
16 KiB
JavaScript
351 lines
16 KiB
JavaScript
const AIReport = (() => {
|
||
// 生成AI报表卡片的HTML
|
||
const generateAICard = () => {
|
||
return `
|
||
<div class="card" style="flex:1;display:flex;flex-direction:column;background:var(--surface);cursor:pointer;position:relative;overflow:hidden" onclick="AIReport.generateReport()">
|
||
<!-- AI图标 -->
|
||
<div style="position:absolute;top:12px;right:12px;width:24px;height:24px;z-index:1">
|
||
<img src="./assets/大模型.png" style="width:24px;height:24px" alt="AI" />
|
||
</div>
|
||
|
||
<!-- 标题 -->
|
||
<div style="font-weight:600;margin-bottom:12px;padding-right:36px;flex-shrink:0;display:flex;align-items:center;gap:8px">
|
||
<img src="./assets/大模型.png" style="width:24px;height:24px" alt="AI" />
|
||
<span style="background:linear-gradient(135deg,#667eea,#764ba2);-webkit-background-clip:text;-webkit-text-fill-color:transparent;font-weight:700">智能报表</span>
|
||
</div>
|
||
|
||
<!-- 内容区域 -->
|
||
<div id="ai-report-content" style="flex:1;min-height:0;overflow:hidden;font-size:13px;line-height:1.6;color:var(--text);display:flex;flex-direction:column">
|
||
<!-- 默认提示 -->
|
||
<div id="ai-report-placeholder" style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;text-align:center;color:var(--text-2)">
|
||
<div style="width:48px;height:48px;background:linear-gradient(135deg,#667eea20,#764ba220);border-radius:12px;display:flex;align-items:center;justify-content:center;margin-bottom:12px">
|
||
<img src="./assets/大模型.png" style="width:32px;height:32px" alt="AI大模型" />
|
||
</div>
|
||
<div style="font-size:14px;margin-bottom:4px">点击生成智能报表</div>
|
||
<div style="font-size:12px">AI将为您分析生产数据并生成洞察</div>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div id="ai-report-loading" style="display:none;height:100%;align-items:center;justify-content:center">
|
||
<div style="text-align:center">
|
||
<div class="banter-loader">
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
</div>
|
||
<div style="color:var(--text-2)">正在思考中,请稍候...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 思考过程 -->
|
||
<div id="ai-report-thinking" style="display:none;flex:1;overflow:hidden;flex-direction:column">
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px;color:var(--text);flex-shrink:0">🤔 AI思考过程</h4>
|
||
<div id="thinking-content" style="background:linear-gradient(135deg,#f8f9ff,#f0f2ff);padding:12px;border-radius:8px;border:1px solid #e0e5ff;flex:1;overflow-y:auto;font-size:12px;line-height:1.6">
|
||
<!-- 思考过程内容 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 报表结果 -->
|
||
<div id="ai-report-result" style="display:none;flex:1;overflow-y:auto">
|
||
<!-- 报表内容 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<div style="margin-top:auto;padding-top:12px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;flex-shrink:0">
|
||
<div style="font-size:11px;color:var(--text-2)">
|
||
<span id="ai-report-time">—</span>
|
||
</div>
|
||
<div style="display:flex;gap:8px">
|
||
<button onclick="event.stopPropagation();AIReport.exportReport()" style="padding:4px 8px;background:var(--surface);border:1px solid var(--border);border-radius:6px;font-size:12px;color:var(--text);cursor:pointer">导出</button>
|
||
<button onclick="event.stopPropagation();AIReport.generateReport()" style="padding:4px 8px;background:linear-gradient(135deg,#667eea,#764ba2);color:white;border:none;border-radius:6px;font-size:12px;cursor:pointer">刷新</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
};
|
||
|
||
// 生成报表
|
||
const generateReport = async () => {
|
||
console.log('generateReport 被调用');
|
||
|
||
const placeholderEl = document.getElementById('ai-report-placeholder');
|
||
const loadingEl = document.getElementById('ai-report-loading');
|
||
const thinkingEl = document.getElementById('ai-report-thinking');
|
||
const resultEl = document.getElementById('ai-report-result');
|
||
const timeEl = document.getElementById('ai-report-time');
|
||
|
||
// 显示思考过程
|
||
if (placeholderEl) placeholderEl.style.display = 'none';
|
||
if (loadingEl) loadingEl.style.display = 'none';
|
||
if (resultEl) resultEl.style.display = 'none';
|
||
|
||
// 确保思考过程容器可见
|
||
if (thinkingEl) {
|
||
thinkingEl.style.display = 'flex';
|
||
thinkingEl.style.flexDirection = 'column';
|
||
thinkingEl.style.flex = '1';
|
||
}
|
||
|
||
// 清空思考内容
|
||
const thinkingContent = document.getElementById('thinking-content');
|
||
console.log('thinkingContent元素:', thinkingContent);
|
||
|
||
if (thinkingContent) {
|
||
thinkingContent.innerHTML = `
|
||
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:200px">
|
||
<div class="banter-loader">
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
<div class="banter-loader__box"></div>
|
||
</div>
|
||
<div style="color:#666;font-style:italic;margin-top:20px">正在思考中,请稍候...</div>
|
||
</div>
|
||
`;
|
||
console.log('设置思考中提示成功');
|
||
} else {
|
||
console.error('找不到thinking-content元素');
|
||
}
|
||
|
||
try {
|
||
// 直接调用分析API,从返回的thinking字段逐步显示
|
||
console.log('开始调用AI API...');
|
||
const response = await fetch('/api/ai/analyze', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const reportData = await response.json();
|
||
console.log('AI响应数据:', reportData);
|
||
|
||
// 如果有思考过程,逐字显示
|
||
if (reportData.thinking && thinkingContent) {
|
||
const thinking = reportData.thinking;
|
||
let displayedText = '';
|
||
const chars = thinking.split('');
|
||
|
||
// 先清空提示文字
|
||
thinkingContent.innerHTML = '';
|
||
|
||
// 逐字显示,速度较慢
|
||
for (let i = 0; i < chars.length; i++) {
|
||
displayedText += chars[i];
|
||
thinkingContent.innerHTML = `
|
||
<div style="line-height:1.8;white-space:pre-wrap">${displayedText}</div>
|
||
`;
|
||
thinkingContent.scrollTop = thinkingContent.scrollHeight;
|
||
|
||
// 每3个字符延迟30ms,让显示更慢
|
||
if (i % 3 === 0) {
|
||
await new Promise(resolve => setTimeout(resolve, 30));
|
||
}
|
||
}
|
||
|
||
// 思考过程显示完成后,等待2秒再显示结果
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
}
|
||
|
||
// 显示结果
|
||
if (thinkingEl) thinkingEl.style.display = 'none';
|
||
if (resultEl) {
|
||
resultEl.style.display = 'block';
|
||
renderReport(reportData);
|
||
}
|
||
|
||
if (timeEl) {
|
||
const now = new Date();
|
||
timeEl.textContent = `更新于 ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('调用AI API失败:', error);
|
||
|
||
// 使用模拟数据作为后备
|
||
console.log('使用模拟数据...');
|
||
|
||
// 模拟思考过程
|
||
if (thinkingContent) {
|
||
thinkingContent.innerHTML = `
|
||
<div style="margin-bottom:8px">1. 数据概览:正在读取生产数据...</div>
|
||
<div style="margin-bottom:8px">2. 数据概览:发现总产量数据,分析良品率...</div>
|
||
<div style="margin-bottom:8px">3. 规律发现:分析平台分布规律...</div>
|
||
<div style="margin-bottom:8px">4. 原因推断:分析质量问题和根本原因...</div>
|
||
<div>5. 结论形成:生成生产建议和预测...</div>
|
||
`;
|
||
}
|
||
|
||
// 延迟后显示模拟结果
|
||
setTimeout(() => {
|
||
if (thinkingEl) thinkingEl.style.display = 'none';
|
||
if (resultEl) {
|
||
resultEl.style.display = 'block';
|
||
resultEl.innerHTML = `
|
||
<div style="padding:4px 0">
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px">📊 生产总览</h4>
|
||
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-bottom:16px">
|
||
<div style="background:var(--bg);padding:8px;border-radius:8px">
|
||
<div style="font-size:11px;color:var(--text-2)">总产量</div>
|
||
<div style="font-size:16px;font-weight:700">15,423</div>
|
||
<div style="font-size:11px;color:#10B981">↑ vs 上周</div>
|
||
</div>
|
||
<div style="background:var(--bg);padding:8px;border-radius:8px">
|
||
<div style="font-size:11px;color:var(--text-2)">良品率</div>
|
||
<div style="font-size:16px;font-weight:700">98.5%</div>
|
||
<div style="font-size:11px;color:var(--text-2)">稳定</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px">📈 平台分布</h4>
|
||
<div style="margin-bottom:8px">
|
||
<div style="display:flex;justify-content:space-between;margin-bottom:2px">
|
||
<span style="font-size:12px">拼多多</span>
|
||
<span style="font-size:12px;font-weight:600">8,934 (57.9%)</span>
|
||
</div>
|
||
<div style="height:4px;background:var(--border);border-radius:2px">
|
||
<div style="width:57.9%;height:100%;background:#3B82F6"></div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-bottom:16px">
|
||
<div style="display:flex;justify-content:space-between;margin-bottom:2px">
|
||
<span style="font-size:12px">圆通</span>
|
||
<span style="font-size:12px;font-weight:600">6,489 (42.1%)</span>
|
||
</div>
|
||
<div style="height:4px;background:var(--border);border-radius:2px">
|
||
<div style="width:42.1%;height:100%;background:#10B981"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px">💡 AI洞察</h4>
|
||
<ul style="margin:0;padding-left:16px;font-size:12px;line-height:1.6">
|
||
<li>本周产量较上周增长12%,主要得益于圆通订单的增加</li>
|
||
<li>良品率保持在98%以上,质量管控效果显著</li>
|
||
<li>建议:继续保持当前生产节奏,关注设备维护</li>
|
||
</ul>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
if (timeEl) {
|
||
const now = new Date();
|
||
timeEl.textContent = `更新于 ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')} (模拟)`;
|
||
}
|
||
}, 2000);
|
||
}
|
||
};
|
||
|
||
// 渲染真实报表数据
|
||
const renderReport = (data) => {
|
||
const html = `
|
||
<div style="padding:4px 0">
|
||
${data.thinking ? `
|
||
<div style="margin-bottom:16px">
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px">🤔 AI思考过程</h4>
|
||
<div style="background:linear-gradient(135deg,#f8f9ff,#f0f2ff);padding:12px;border-radius:8px;border:1px solid #e0e5ff;font-size:12px;line-height:1.6">
|
||
${data.thinking}
|
||
</div>
|
||
</div>
|
||
` : ''}
|
||
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px">📊 生产总览</h4>
|
||
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-bottom:16px">
|
||
<div style="background:var(--bg);padding:8px;border-radius:8px">
|
||
<div style="font-size:11px;color:var(--text-2)">总产量</div>
|
||
<div style="font-size:16px;font-weight:700">${data.summary?.totalProduction?.toLocaleString() || 'N/A'}</div>
|
||
<div style="font-size:11px;color:${data.summary?.trend === 'up' ? '#10B981' : '#EF4444'}">${data.summary?.trend === 'up' ? '↑' : '↓'} vs 上周</div>
|
||
</div>
|
||
<div style="background:var(--bg);padding:8px;border-radius:8px">
|
||
<div style="font-size:11px;color:var(--text-2)">良品率</div>
|
||
<div style="font-size:16px;font-weight:700">${data.summary?.goodRate || 'N/A'}</div>
|
||
<div style="font-size:11px;color:var(--text-2)">稳定</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px">📈 平台分布</h4>
|
||
${data.platforms ? Object.entries(data.platforms).map(([platform, info]) => `
|
||
<div style="margin-bottom:8px">
|
||
<div style="display:flex;justify-content:space-between;margin-bottom:2px">
|
||
<span style="font-size:12px">${platform === 'pdd' ? '拼多多' : '圆通'}</span>
|
||
<span style="font-size:12px;font-weight:600">${info.count?.toLocaleString() || 'N/A'} (${info.percentage || 'N/A'}%)</span>
|
||
</div>
|
||
<div style="height:4px;background:var(--border);border-radius:2px">
|
||
<div style="width:${info.percentage || 0}%;height:100%;background:${platform === 'pdd' ? '#3B82F6' : '#10B981'}"></div>
|
||
</div>
|
||
</div>
|
||
`).join('') : '<div style="font-size:12px;color:var(--text-2)">暂无平台数据</div>'}
|
||
|
||
${data.summary?.insights ? `
|
||
<h4 style="font-size:14px;font-weight:600;margin-bottom:8px;margin-top:16px">💡 AI洞察</h4>
|
||
<ul style="margin:0;padding-left:16px;font-size:12px;line-height:1.6">
|
||
${data.summary.insights.map(insight => {
|
||
// 检查是否包含警告标记
|
||
const hasWarning = insight.includes('⚠️');
|
||
const style = hasWarning ?
|
||
'background:linear-gradient(135deg,#FEF3C7,#FDE68A);padding:8px;border-radius:6px;border-left:3px solid #F59E0B;margin-bottom:8px;font-weight:600;color:#92400E' :
|
||
'margin-bottom:4px';
|
||
return `<li style="${style}">${insight}</li>`;
|
||
}).join('')}
|
||
</ul>
|
||
` : ''}
|
||
</div>
|
||
`;
|
||
|
||
const resultEl = document.getElementById('ai-report-result');
|
||
if (resultEl) {
|
||
resultEl.innerHTML = html;
|
||
}
|
||
};
|
||
|
||
// 导出报表
|
||
const exportReport = () => {
|
||
const resultEl = document.getElementById('ai-report-result');
|
||
if (!resultEl || resultEl.style.display === 'none') {
|
||
alert('请先生成报表');
|
||
return;
|
||
}
|
||
|
||
const textContent = resultEl.innerText || resultEl.textContent;
|
||
const blob = new Blob([`智能生产报表\n生成时间:${new Date().toLocaleString()}\n\n${textContent}`], { type: 'text/plain' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `AI生产报表_${new Date().toISOString().split('T')[0]}.txt`;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
};
|
||
|
||
// 暴露方法
|
||
return {
|
||
generateAICard,
|
||
generateReport,
|
||
exportReport
|
||
};
|
||
})();
|
||
|
||
// 添加样式
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
`;
|
||
document.head.appendChild(style); |