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=``; + html += th; + }); + html +="" + + //数据的遍历 + data.forEach(function(row, index) { + html +="" + //行数据 + rsp.cols.forEach(function(item, index) { + let td=``; + html += td; + }); + html += ""; + }); + html +="
${item}
${row[item]}
"; + + 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 += `

${ret.data.alt}

`; + 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)} +}