From ecd0fe82d4c735578fa78020800be5b037ae8a9d Mon Sep 17 00:00:00 2001
From: 13315423919 <13315423919@qq.com>
Date: Wed, 19 Nov 2025 19:43:10 +0800
Subject: [PATCH] Add File
---
main/ui/js/draw_chart.js | 664 +++++++++++++++++++++++++++++++++++++++
1 file changed, 664 insertions(+)
create mode 100644 main/ui/js/draw_chart.js
diff --git a/main/ui/js/draw_chart.js b/main/ui/js/draw_chart.js
new file mode 100644
index 0000000..2c9280c
--- /dev/null
+++ b/main/ui/js/draw_chart.js
@@ -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=`
${rsp.desc}
`
+
+ //表头
+ rsp.cols.forEach(function(item, index) {
+ let th=`| ${item} | `;
+ html += th;
+ });
+ html +="
"
+
+ //数据的遍历
+ data.forEach(function(row, index) {
+ html +=""
+ //行数据
+ rsp.cols.forEach(function(item, index) {
+ let td=`| ${row[item]} | `;
+ html += td;
+ });
+ html += "
";
+ });
+ html +="
";
+
+ 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=`K3GPT数据分析智能体
+ ${file_name}
+ 制作日期: ${getFormattedDate(today)}
`;
+
+ ppt += `用户需求
${req}
`;
+
+ var table_index=1;
+
+ //分析过程
+ if (Object.keys(chart_datas[chat_id]).length != 0){
+ ppt += `数据分析
`;
+ let obj = chart_datas[chat_id];
+ for (let key in obj) {
+ if (key.startsWith("Table_")){
+ ppt += `表格${table_index++}
`;
+ ppt += obj[key];
+ }else{
+ ppt += `${key}
`;
+ ppt += `[chart][bar]
${obj[key]}
`;
+ }
+ }
+ }
+ //结论
+ ppt += `小结
${rsp}`;
+
+ return ppt;
+}
+
+//生成ppt,单独的文件,使用前端报表的图片
+
+async function gen_ppt_by_img(file_name,req,rsp,chat_id){
+
+ const today = new Date();
+
+ var ppt=`K3GPT数据分析智能体
+ ${file_name}
+ 制作日期: ${getFormattedDate(today)}
`;
+
+ ppt += `用户需求
${req}
`;
+
+ var table_index=1;
+
+ //分析过程
+ if (Object.keys(chart_datas[chat_id]).length != 0){
+ ppt += `数据分析
`;
+ let obj = chart_datas[chat_id];
+ for (let key in obj) {
+ if (key.startsWith("Table_")){
+ ppt += `表格${table_index++}
`;
+ ppt += obj[key];
+ }else{
+ ppt += `${key}
`;
+
+ //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 += `
`;
+ console.info(ppt);
+ }else{
+ ppt += `图表数据:${ret.message}
`;
+ }
+ //});
+
+ }
+ }
+ }
+ //结论
+ ppt += `小结
${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 = `用户需求
${req}
`;
+
+ var ppt="";
+
+ var table_index=1;
+
+ //分析过程
+ if (Object.keys(chart_datas[chat_id]).length != 0){
+ ppt += `数据分析
`;
+ let obj = chart_datas[chat_id];
+ for (let key in obj) {
+ if (key.startsWith("Table_")){
+ ppt += `表格${table_index++}
`;
+ ppt += obj[key];
+ }else{
+ ppt += `${key}
`;
+ ppt += `[chart][bar]
${obj[key]}
`;
+ }
+ }
+ }
+ //结论
+ ppt += `小结
${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)}
+}