This commit is contained in:
2025-11-19 19:43:10 +08:00
parent eb486b445c
commit ecd0fe82d4

664
main/ui/js/draw_chart.js Normal file
View File

@@ -0,0 +1,664 @@
/*
图表表格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)}
}