Files
k3GPT/main/ui/js/draw_chart.js
2025-11-19 19:43:10 +08:00

665 lines
21 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
图表表格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)}
}