665 lines
21 KiB
JavaScript
665 lines
21 KiB
JavaScript
/*
|
||
|
||
图表,表格,PPT生成的入口函数
|
||
|
||
|
||
*/
|
||
|
||
//图表
|
||
function draw_chart(name,rsp,type){
|
||
var ctx = document.getElementById(name).getContext('2d');
|
||
const data = JSON.parse(rsp.data);
|
||
var result={};
|
||
if (type=="do_group_count")
|
||
{
|
||
if (rsp.cols.length==2){ //普通柱状图
|
||
result=draw_chart_bar(ctx,data,rsp.cols);
|
||
|
||
}else if(rsp.cols.length==3){ // 堆叠柱状图,
|
||
result=draw_chart_bar_stack(ctx,data,rsp.cols);
|
||
|
||
}else{ //三个及以上,使用一维方式表达
|
||
result=draw_chart_bar_on3(ctx,data,rsp.cols);
|
||
}
|
||
|
||
}//end if
|
||
|
||
if (["do_group_agg","do_fields_agg"].includes(type)){
|
||
if(rsp.cols.length==7){ // 分组柱状图,一个维度,6个指标
|
||
result=draw_chart_bar_by_d6(ctx,data,rsp.cols,rsp.field);
|
||
}else if(rsp.cols.length==8){ //分组堆积图,两个个维度,6个指标
|
||
result=draw_chart_bar_by_d6_2(ctx,data,rsp.cols,rsp.field);
|
||
}
|
||
}//end do_group_sum
|
||
return result;
|
||
}//end funciton draw_chart
|
||
|
||
|
||
//表格
|
||
function draw_table(name,rsp,desc){
|
||
var div = document.getElementById(name)
|
||
const data = JSON.parse(rsp.data);
|
||
|
||
//console.info(data);
|
||
|
||
var html=`<p>${rsp.desc}</p><table><tr>`
|
||
|
||
//表头
|
||
rsp.cols.forEach(function(item, index) {
|
||
let th=`<th>${item}</th>`;
|
||
html += th;
|
||
});
|
||
html +="</tr>"
|
||
|
||
//数据的遍历
|
||
data.forEach(function(row, index) {
|
||
html +="<tr>"
|
||
//行数据
|
||
rsp.cols.forEach(function(item, index) {
|
||
let td=`<td>${row[item]}</td>`;
|
||
html += td;
|
||
});
|
||
html += "</tr>";
|
||
});
|
||
html +="</table>";
|
||
|
||
div.innerHTML=html;
|
||
|
||
return html;
|
||
}
|
||
|
||
|
||
function getFormattedDate(date) {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero based
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
|
||
return `${year}-${month}-${day}`;
|
||
}
|
||
|
||
|
||
|
||
|
||
//生成ppt,单独的文件,使用数据方式
|
||
function gen_ppt(file_name,req,rsp,chat_id){
|
||
|
||
const today = new Date();
|
||
|
||
var ppt=`<h1>K3GPT数据分析智能体</h1>
|
||
<h3>${file_name}</h3>
|
||
<p>制作日期: ${getFormattedDate(today)}</p>`;
|
||
|
||
ppt += `<h2>用户需求</h2><h5>${req}</h5>`;
|
||
|
||
var table_index=1;
|
||
|
||
//分析过程
|
||
if (Object.keys(chart_datas[chat_id]).length != 0){
|
||
ppt += `<h2>数据分析</h2>`;
|
||
let obj = chart_datas[chat_id];
|
||
for (let key in obj) {
|
||
if (key.startsWith("Table_")){
|
||
ppt += `<h3>表格${table_index++}</h3>`;
|
||
ppt += obj[key];
|
||
}else{
|
||
ppt += `<h3>${key}</h3>`;
|
||
ppt += `<p>[chart][bar]</p><pre><code>${obj[key]}</code></pre>`;
|
||
}
|
||
}
|
||
}
|
||
//结论
|
||
ppt += `<h2>小结</h2>${rsp}`;
|
||
|
||
return ppt;
|
||
}
|
||
|
||
//生成ppt,单独的文件,使用前端报表的图片
|
||
|
||
async function gen_ppt_by_img(file_name,req,rsp,chat_id){
|
||
|
||
const today = new Date();
|
||
|
||
var ppt=`<h1>K3GPT数据分析智能体</h1>
|
||
<h3>${file_name}</h3>
|
||
<p>制作日期: ${getFormattedDate(today)}</p>`;
|
||
|
||
ppt += `<h2>用户需求</h2><h5>${req}</h5>`;
|
||
|
||
var table_index=1;
|
||
|
||
//分析过程
|
||
if (Object.keys(chart_datas[chat_id]).length != 0){
|
||
ppt += `<h2>数据分析</h2>`;
|
||
let obj = chart_datas[chat_id];
|
||
for (let key in obj) {
|
||
if (key.startsWith("Table_")){
|
||
ppt += `<h3>表格${table_index++}</h3>`;
|
||
ppt += obj[key];
|
||
}else{
|
||
ppt += `<h3>${key}</h3>`;
|
||
|
||
//postChartImageData(chart_ids[chat_id][key],key).then(ret =>{
|
||
var ret = await postChartImageData(chart_ids[chat_id][key],key);
|
||
console.info(ret);
|
||
if (ret.errno==0){
|
||
ppt += `<p><img src="${ret.data.url}" alt="${ret.data.alt}" style="width:50%;" /></p>`;
|
||
console.info(ppt);
|
||
}else{
|
||
ppt += `<p>图表数据:${ret.message}</p>`;
|
||
}
|
||
//});
|
||
|
||
}
|
||
}
|
||
}
|
||
//结论
|
||
ppt += `<h2>小结</h2>${rsp}`;
|
||
return ppt;
|
||
}
|
||
|
||
|
||
//提交图表的图片数据
|
||
async function postChartImageData(name,title) {
|
||
const url = '/api/img_base64';
|
||
|
||
const chartCanvas = document.getElementById(name);
|
||
const imageBase64 = chartCanvas.toDataURL('image/png');
|
||
|
||
const data = {
|
||
img_base64_data: imageBase64,
|
||
alias: title
|
||
};
|
||
|
||
|
||
|
||
const response = await axios.post(url, data, {
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded'
|
||
}
|
||
});
|
||
|
||
|
||
return response.data;
|
||
}
|
||
|
||
|
||
//生成ppt,不带封面
|
||
function gen_ppt_slides(req,rsp,chat_id){
|
||
|
||
//暂时去掉用户需求,和引导页有些重复
|
||
//var ppt = `<h2>用户需求</h2><h5>${req}</h5>`;
|
||
|
||
var ppt="";
|
||
|
||
var table_index=1;
|
||
|
||
//分析过程
|
||
if (Object.keys(chart_datas[chat_id]).length != 0){
|
||
ppt += `<h2>数据分析</h2>`;
|
||
let obj = chart_datas[chat_id];
|
||
for (let key in obj) {
|
||
if (key.startsWith("Table_")){
|
||
ppt += `<h3>表格${table_index++}</h3>`;
|
||
ppt += obj[key];
|
||
}else{
|
||
ppt += `<h3>${key}</h3>`;
|
||
ppt += `<p>[chart][bar]</p><pre><code>${obj[key]}</code></pre>`;
|
||
}
|
||
}
|
||
}
|
||
//结论
|
||
ppt += `<h2>小结</h2>${rsp}`;
|
||
|
||
return ppt;
|
||
}
|
||
|
||
|
||
// 扩展后的背景颜色组
|
||
let backgroundColor = [
|
||
'rgba(255, 99, 132, 0.2)',
|
||
'rgba(54, 162, 235, 0.2)',
|
||
'rgba(255, 206, 86, 0.2)',
|
||
'rgba(75, 192, 192, 0.2)',
|
||
'rgba(153, 102, 255, 0.2)',
|
||
'rgba(255, 159, 64, 0.2)',
|
||
// 新增的颜色
|
||
'rgba(255, 99, 71, 0.2)', // 番茄红
|
||
'rgba(144, 238, 144, 0.2)', // 淡绿
|
||
'rgba(173, 216, 230, 0.2)', // 浅天蓝
|
||
'rgba(255, 192, 203, 0.2)' // 浅粉
|
||
];
|
||
|
||
// 扩展后的边框颜色组
|
||
let borderColor = [
|
||
'rgba(255,99,132,1)',
|
||
'rgba(54, 162, 235, 1)',
|
||
'rgba(255, 206, 86, 1)',
|
||
'rgba(75, 192, 192, 1)',
|
||
'rgba(153, 102, 255, 1)',
|
||
'rgba(255, 159, 64, 1)',
|
||
// 新增的颜色
|
||
'rgba(255, 99, 71, 1)', // 番茄红
|
||
'rgba(144, 238, 144, 1)', // 淡绿
|
||
'rgba(173, 216, 230, 1)', // 浅天蓝
|
||
'rgba(255, 192, 203, 1)' // 浅粉
|
||
];
|
||
|
||
//柱状图,一个维度,一个count
|
||
function draw_chart_bar(ctx,data,cols){
|
||
var title = cols[0]+"分布";
|
||
var groups = data.map(item => item[cols[0]]);
|
||
var values = data.map(item => item.count);
|
||
var myChart = new Chart(ctx, {
|
||
type: 'bar',
|
||
data: {
|
||
labels: groups,
|
||
datasets: [{
|
||
data: values,
|
||
backgroundColor: backgroundColor,
|
||
borderColor:borderColor,
|
||
borderWidth: 1
|
||
}]
|
||
},
|
||
options: {
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: title,
|
||
position: 'top', // 可选:'top', 'left', 'bottom', 'right'
|
||
font: {
|
||
size: 16,
|
||
family: 'Helvetica',
|
||
weight: 'bold',
|
||
color: '#333'
|
||
}
|
||
},
|
||
legend: {
|
||
display: false, //不显示图例
|
||
position: 'top'
|
||
}
|
||
},
|
||
}
|
||
});
|
||
return {"title":title,"data":JSON.stringify({"categories":groups,"count":values})}
|
||
}
|
||
|
||
//堆叠柱状图,两个维度,一个count
|
||
function draw_chart_bar_stack(ctx,data,cols){
|
||
//使用一个map的结构,来分离不同的数据
|
||
var title = cols[0]+"->"+cols[1]+"的分布";
|
||
var city = cols[0]; //第一个字段名称
|
||
var area = cols[1]; //第二个字段名称
|
||
// 创建一个映射来存储结果
|
||
let resultMap = {};
|
||
data.forEach(item => {
|
||
if (!resultMap[item[city]]) {
|
||
resultMap[item[city]] = {}; // 初始化城市
|
||
}
|
||
resultMap[item[city]][item[area]] = item.count; // 填充数据
|
||
});
|
||
//console.info(resultMap);
|
||
// 准备Chart.js所需的datasets和labels
|
||
let labels = [];
|
||
let datasets = [];
|
||
|
||
let index=0;
|
||
|
||
// 获取所有不同的area名称
|
||
let areas = new Set();
|
||
for (let city in resultMap) {
|
||
for (let area in resultMap[city]) {
|
||
areas.add(area);
|
||
}
|
||
labels.push(city); // 添加城市作为标签
|
||
}
|
||
areas.forEach(area => {
|
||
let dataset = {
|
||
label: area,
|
||
data: [],
|
||
stack: 'Stack 0', // 所有数据集属于同一堆栈
|
||
backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index],
|
||
borderWidth: 1
|
||
};
|
||
labels.forEach(city => {
|
||
dataset.data.push(resultMap[city][area] || 0); // 如果该城市没有对应区域的数据,则填充0
|
||
});
|
||
datasets.push(dataset);
|
||
index ++;
|
||
});
|
||
//console.info(labels);
|
||
//console.info(datasets);
|
||
|
||
//画图
|
||
var myChart = new Chart(ctx, {
|
||
type: 'bar', // 或者 'line' 来创建堆叠折线图
|
||
data: {
|
||
labels: labels, // X轴标签
|
||
datasets: datasets,
|
||
},
|
||
options: {
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: title,
|
||
position: 'top', // 可选:'top', 'left', 'bottom', 'right'
|
||
font: {
|
||
size: 16,
|
||
family: 'Helvetica',
|
||
weight: 'bold',
|
||
color: '#333'
|
||
}
|
||
},
|
||
legend: {
|
||
display: true,
|
||
labels: {
|
||
// 自定义生成图例项
|
||
generateLabels: function(chart) {
|
||
const original = Chart.defaults.plugins.legend.labels.generateLabels(chart);
|
||
// 只保留前6个图例项
|
||
return original.slice(0, 6);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
stacked: true // 设置X轴为堆叠(适用于柱状图)
|
||
},
|
||
y: {
|
||
stacked: true// 设置Y轴为堆叠
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// ppt 数据
|
||
var series={}
|
||
|
||
datasets.forEach(item=>{
|
||
series[item.label] = item.data;
|
||
})
|
||
series["categories"] = labels;
|
||
return {"title":title,"data":JSON.stringify(series)}
|
||
}
|
||
|
||
function draw_chart_bar_on3(ctx,data,cols){
|
||
|
||
var groups = data.map(item => item[cols[0]]+'->'+item[cols[1]]+'->'+item[cols[2]]);
|
||
var title = cols[0]+"->"+cols[1]+"->"+cols[2]+"等的分布分析";
|
||
var values = data.map(item => item.count);
|
||
|
||
var myChart = new Chart(ctx, {
|
||
type: 'bar',
|
||
data: {
|
||
labels: groups,
|
||
datasets: [{
|
||
data: values,
|
||
backgroundColor: backgroundColor,
|
||
borderColor:borderColor,
|
||
borderWidth: 1
|
||
}]
|
||
},
|
||
options: {
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: title,
|
||
position: 'top', // 可选:'top', 'left', 'bottom', 'right'
|
||
font: {
|
||
size: 16,
|
||
family: 'Helvetica',
|
||
weight: 'bold',
|
||
color: '#333'
|
||
}
|
||
},
|
||
legend: {
|
||
display: false, //不显示图例
|
||
position: 'top'
|
||
}
|
||
},
|
||
}
|
||
});
|
||
return {"title":title,"data":JSON.stringify({"categories":groups,"count":values})}
|
||
}
|
||
|
||
//分组柱状图,一个维度,6个指标
|
||
function draw_chart_bar_by_d6(ctx,data,cols,field){
|
||
//标题
|
||
var title = "不同"+cols[0]+"对应的"+field+"的常见六指标分析";
|
||
|
||
// 准备结果对象
|
||
let index=0;
|
||
let result = {
|
||
labels: [],
|
||
datasets: [
|
||
{label: '总和', type:"line", data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-1'},
|
||
{label: '平均值', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '中位数', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '最小值', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '最大值', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '个数', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
]
|
||
};
|
||
|
||
// 对原始数据按级别排序(可选),一个维度
|
||
data.sort((a, b) => a[cols[0]].localeCompare(b[cols[0]]));
|
||
|
||
// 填充labels和datasets的数据
|
||
data.forEach(item => {
|
||
result.labels.push(item[cols[0]]);
|
||
result.datasets[0].data.push(item.total);
|
||
result.datasets[1].data.push(item.avg);
|
||
result.datasets[2].data.push(item.median);
|
||
result.datasets[3].data.push(item.min);
|
||
result.datasets[4].data.push(item.max);
|
||
result.datasets[5].data.push(item.count);
|
||
});
|
||
//画图
|
||
var myChart = new Chart(ctx, {
|
||
type: 'bar', // 或者 'line' 来创建堆叠折线图
|
||
data: result,
|
||
options: {
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: title,
|
||
position: 'top', // 可选:'top', 'left', 'bottom', 'right'
|
||
font: {
|
||
size: 16,
|
||
family: 'Helvetica',
|
||
weight: 'bold',
|
||
color: '#333'
|
||
}
|
||
},
|
||
legend: {
|
||
display: true,
|
||
labels: {
|
||
// 自定义生成图例项
|
||
generateLabels: function(chart) {
|
||
const original = Chart.defaults.plugins.legend.labels.generateLabels(chart);
|
||
// 只保留前6个图例项
|
||
return original.slice(0, 6);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
stacked: false // 设置X轴为堆叠(适用于柱状图)
|
||
},
|
||
// 左边 Y 轴(用于 Total)
|
||
'y-1': {
|
||
type: 'linear',
|
||
display: true,
|
||
position: 'right',
|
||
title: {
|
||
display: true,
|
||
text: '总量'
|
||
},
|
||
grid: {
|
||
drawOnChartArea: false // 是否绘制网格线
|
||
}
|
||
},
|
||
// 右边 Y 轴(用于 Average、Count 等)
|
||
'y-2': {
|
||
type: 'linear',
|
||
display: true,
|
||
position: 'left',
|
||
title: {
|
||
display: true,
|
||
text: field,
|
||
},
|
||
grid: {
|
||
drawOnChartArea: true // 不在右侧画网格线
|
||
}
|
||
},
|
||
}
|
||
}
|
||
});
|
||
|
||
// ppt 数据
|
||
var series={}
|
||
series["categories"] = result.labels;
|
||
series["总和"] = result.datasets[0].data;
|
||
series["平均值"] = result.datasets[1].data;
|
||
series["中位数"] = result.datasets[2].data;
|
||
series["最小值"] = result.datasets[3].data;
|
||
series["最大值"] = result.datasets[4].data;
|
||
series["个数"] = result.datasets[5].data;
|
||
return {"title":title,"data":JSON.stringify(series)}
|
||
}
|
||
|
||
|
||
//分组柱状图,两个维度,6个指标
|
||
function draw_chart_bar_by_d6_2(ctx,data,cols,field){
|
||
//标题
|
||
var title = cols[0]+"->"+cols[1]+"对应的"+field+"的常见六指标分析";
|
||
|
||
// 准备结果对象
|
||
let index=0;
|
||
let result = {
|
||
labels: [],
|
||
datasets: [
|
||
{label: '总和', type:"line", data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-1'},
|
||
{label: '平均值', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '中位数', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '最小值', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '最大值', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
{label: '个数', data: [],backgroundColor: backgroundColor[index], // 可以为每个区域分配不同颜色
|
||
borderColor: borderColor[index++],
|
||
borderWidth: 1,yAxisID: 'y-2',},
|
||
]
|
||
};
|
||
|
||
// 对原始数据按级别排序(可选),一个维度
|
||
data.sort((a, b) => a[cols[0]].localeCompare(b[cols[0]]));
|
||
|
||
// 填充labels和datasets的数据
|
||
data.forEach(item => {
|
||
result.labels.push(item[cols[0]]+"->"+item[cols[1]]);
|
||
result.datasets[0].data.push(item.total);
|
||
result.datasets[1].data.push(item.avg);
|
||
result.datasets[2].data.push(item.median);
|
||
result.datasets[3].data.push(item.min);
|
||
result.datasets[4].data.push(item.max);
|
||
result.datasets[5].data.push(item.count);
|
||
});
|
||
//画图
|
||
var myChart = new Chart(ctx, {
|
||
type: 'bar', // 或者 'line' 来创建堆叠折线图
|
||
data: result,
|
||
options: {
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: title,
|
||
position: 'top', // 可选:'top', 'left', 'bottom', 'right'
|
||
font: {
|
||
size: 16,
|
||
family: 'Helvetica',
|
||
weight: 'bold',
|
||
color: '#333'
|
||
}
|
||
},
|
||
legend: {
|
||
display: true,
|
||
labels: {
|
||
// 自定义生成图例项
|
||
generateLabels: function(chart) {
|
||
const original = Chart.defaults.plugins.legend.labels.generateLabels(chart);
|
||
// 只保留前6个图例项
|
||
return original.slice(0, 6);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
stacked: false // 设置X轴为堆叠(适用于柱状图)
|
||
},
|
||
// 左边 Y 轴(用于 Total)
|
||
'y-1': {
|
||
type: 'linear',
|
||
display: true,
|
||
position: 'right',
|
||
title: {
|
||
display: true,
|
||
text: '总量'
|
||
},
|
||
grid: {
|
||
drawOnChartArea: false // 是否绘制网格线
|
||
}
|
||
},
|
||
// 右边 Y 轴(用于 Average、Count 等)
|
||
'y-2': {
|
||
type: 'linear',
|
||
display: true,
|
||
position: 'left',
|
||
title: {
|
||
display: true,
|
||
text: field,
|
||
},
|
||
grid: {
|
||
drawOnChartArea: true // 不在右侧画网格线
|
||
}
|
||
},
|
||
}
|
||
}
|
||
});
|
||
|
||
// ppt 数据
|
||
// ppt 数据
|
||
var series={}
|
||
series["categories"] = result.labels;
|
||
series["总和"] = result.datasets[0].data;
|
||
series["平均值"] = result.datasets[1].data;
|
||
series["中位数"] = result.datasets[2].data;
|
||
series["最小值"] = result.datasets[3].data;
|
||
series["最大值"] = result.datasets[4].data;
|
||
series["个数"] = result.datasets[5].data;
|
||
return {"title":title,"data":JSON.stringify(series)}
|
||
}
|