feat(数据可视化技术): 添加网店运营大屏前端和后端基础结构

- 新建前端项目配置文件和样式文件
- 创建后端 Flask 应用和数据库连接管理器
- 定义多个 API 路由以获取销售数据
This commit is contained in:
2025-05-19 09:12:20 +08:00
parent 265128110d
commit c61ecd74cd
30 changed files with 9132 additions and 0 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,128 @@
// 发送 AJAX 请求获取数据
// 这段代码的作用是从指定的 API 端点http://127.0.0.1:5000/api/sales_manager获取销售经理相关的数据
// 并将这些数据整理成三个独立的数组sales_data、profit_data 和 sales_manager。
fetch('http://127.0.0.1:5000/api/sales_manager')
.then(response => response.json())
.then(data => {
var sales_data = [];
var profit_data = [];
var sales_manager = [];
data.forEach(item => {
temp = {};
temp1 = {};
temp['value'] = parseFloat(item.sales);
temp['name'] = item.sales_manager;
temp1['value'] = parseFloat(item.profit);
temp1['name'] = item.sales_manager;
sales_data.push(temp);
profit_data.push(temp1);
sales_manager.push(item.sales_manager);
});
console.log("======sales_manager======");
console.log(sales_manager);
console.log(sales_data);
console.log(profit_data);
var myChart = echarts.init(document.getElementById('data_manager'));
var option = {
color: ["#EAEA26", "#906BF9", "#FE5656", "#01E17E", "#3DD1F9", "#FFAD05"],
title: {
text: ' 销售情况 利润情况',
left: 'left',
textStyle: {
color: '#0099FF',
fontSize: 10,
}
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
left: 'center',
top: 'bottom',
data: sales_manager,
textStyle: {
color: ["#EAEA26", "#906BF9", "#FE5656", "#01E17E", "#3DD1F9", "#FFAD05"],
},
padding: 0,
itemGap: 3,
itemWidth: 30,
},
series: [
{
name: '销售额',
type: 'pie',
radius: [5, 85],
center: ['30%', '50%'],
roseType: 'area',
label: {
show: false
},
labelLine: {
normal: {
show: false,
},
emphasis: {
show: false
}
},
emphasis: {
label: {
show: false
}
},
data: sales_data,
},
{
name: '利润',
type: 'pie',
radius: [5, 85],
center: ['80%', '50%'],
roseType: 'area',
label: {
show: false,
},
labelLine: {
normal: {
show: false,
},
emphasis: {
show: false
}
},
data: profit_data,
}
]
};
myChart.setOption(option);
var currentIndex = -1;
var pie_index = -1;
var pie_index1 = 0
setInterval(function () {
var dataLen = option.series[1].data.length;
pie_index = pie_index == 11 ? 0 : (pie_index + 1)
// console.log(currentIndex1)
// 取消之前高亮的图形
myChart.dispatchAction({
type: 'downplay',
seriesIndex: pie_index1,
dataIndex: currentIndex
});
currentIndex = (currentIndex + 1) % dataLen;
pie_index1 = parseInt(pie_index / 6)
// 高亮当前图形
myChart.dispatchAction({
type: 'highlight',
seriesIndex: pie_index1,
dataIndex: currentIndex
});
// 显示 tooltip
myChart.dispatchAction({
type: 'showTip',
seriesIndex: pie_index1,
dataIndex: currentIndex
});
}, 2000);
})

View File

@@ -0,0 +1,190 @@
// 发送 AJAX 请求获取数据
fetch('http://127.0.0.1:5000/api/sales_month')
.then(response => response.json())
.then(data => {
var month = [];
var sales = [];
var profit = [];
data.forEach(item => {
month.push(item.month);
profit.push(parseFloat((item.profit / 10000).toFixed(2)));
sales.push(parseFloat((item.sales / 10000).toFixed(2)));
});
console.log("======sales_month======")
console.log(month);
console.log(sales);
console.log(profit);
var myChart = echarts.init(document.getElementById('data_month'));
var option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data: ['销售额(万元)', '利润(万元)'],
x: 'center',
y: 'top',
padding: [5, 0, 0, 0],
textStyle: {
color: '#00F1F3', // 修改为正确的颜色代码
fontSize: 10,
},
itemWidth: 28,
itemHeight: 12,
},
grid: {
show: false,
left: 0,
right: 20,
bottom: 0,
top: '15%',
containLabel: true,
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'line',
label: {
show: true,
fontSize: 8,
},
lineStyle: {
color: '#2094CA',
type: 'dotted',
},
},
textStyle: {
fontSize: 10,
},
},
xAxis: [{
type: 'category',
axisLabel: {
interval: 0,
fontSize: 8,
color: '#2094CA',
},
data: month,
}],
yAxis: [{
splitLine: { show: false },
type: 'value',
axisLabel: {
fontSize: 8,
color: '#2094CA',
},
}],
series: [{
name: '销售额(万元)',
type: 'line',
symbol: 'circle',
symbolSize: 4,
itemStyle: {
normal: {
color: '#E78932',
},
},
lineStyle: {
color: '#E78932',
width: 1,
},
label: {
show: false,
position: 'top',
textStyle: {
color: '#E78932',
},
fontSize: 8,
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: '#D98234' // 0% 处的颜色
}, {
offset: 1, color: '#33313C' // 100% 处的颜色
}],
global: false // 缺省为 false
},
opacity: 0.5,
},
data: sales,
showAllSymbol: true,
}, {
name: '利润(万元)',
type: 'line',
symbol: 'circle',
symbolSize: 4,
itemStyle: {
normal: {
color: '#0099FF',
},
},
label: {
show: false,
position: 'top',
textStyle: {
color: '#0099FF',
},
fontSize: 8,
},
lineStyle: {
color: '#0099FF',
width: 0,
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: '#0099FF' // 0% 处的颜色
}, {
offset: 1, color: '#394E7F' // 100% 处的颜色
}],
global: false // 缺省为 false
},
opacity: 0.3,
},
data: profit,
showAllSymbol: true,
}]
};
myChart.setOption(option);
var currentIndex = -1;
setInterval(function () {
var dataLen = option.series[0].data.length;
// 取消之前高亮的图形
myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: currentIndex
});
currentIndex = (currentIndex + 1) % dataLen;
// 高亮当前图形
myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: currentIndex
});
// 显示 tooltip
myChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: currentIndex
});
}, 2000);
})
.catch(error => console.error('Error fetching data:', error)); // 捕获并处理错误

View File

@@ -0,0 +1,110 @@
// 发送 AJAX 请求获取数据
fetch('http://127.0.0.1:5000/api/sales_product')
.then(response => response.json())
.then(data => {
console.log("sales_product"+data)
var myChart = echarts.init(document.getElementById('data_product'));
var color = [
"#00C6FB",
"#5781FD",
"#4DB1CB",
// "#3EBD7C",
// "#F7A925",
// "#bda29a",
// "#ca8622",
// "#749f83",
// "#6e7074",
// "#546570",
// "#c4ccd3"
];
option = {
tooltip: {
// axisPointer: { // 坐标轴指示器,坐标轴触发有效
// type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
// },
// textStyle:{
// color:'#0099FF',
// fontSize:10,
// }
},
series: [{
name: '销售额',
type: 'treemap',
roam:false,
nodeClick:false,
breadcrumb:false,
left:0,
right:0,
top:0,
bottom:0,
itemStyle: {
borderColor: '#062e62'
},
label:{
fontSize:8,
color:'#fff',
},
levels: [{
color: color,
itemStyle: {
normal: {
borderWidth: 0,
borderColor: '#062e62',
gapWidth: 2
}
}
},
{
//colorSaturation: [0.35, 0.6],
colorAlpha: [1, 0.5],
upperLabel: {
normal: {
color: '#00C6FB',
fontSize:10,
show: true,
height: 15
}
},
itemStyle: {
normal: {
borderWidth: 5,
borderColor: '#062e62',
gapWidth: 1,
},
emphasis: {
borderColor: '#ccc'
}
}
}
],
leafDepth: 2,
data: data
}]
};
//指定的配置项
myChart.setOption(option)
var currentIndex = -1;
setInterval(function () {
var dataLen = option.series[0].data.length;
// 取消之前高亮的图形
myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: currentIndex
});
currentIndex = (currentIndex + 1) % dataLen;
// 高亮当前图形
myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: currentIndex
});
// 显示 tooltip
myChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: currentIndex
});
}, 1000);
})

View File

@@ -0,0 +1,253 @@
// 发送 AJAX 请求获取数据
fetch('http://127.0.0.1:5000/api/sales_province')
.then(response => response.json())
.then(data => {
var sales = [];
//将数据整理为地图的数据
var sales_province = [];
var profit_province = [];
data.forEach(item => {
sales.push(parseFloat(item.sales));
sales_set = {};
profit_set = {};
sales_set['name'] = item.province;
sales_set['value'] = item.sales;
profit_set['name'] = item.province;
profit_set['value'] = item.profit;
sales_province.push(sales_set);
profit_province.push(profit_set);
});
//添加总销售额的数据,用于在图表上方显示
var sales_total = Math.round(d3.sum(sales) / 10000);
console.log("=======sales_province========");
console.log(sales_province);
console.log("=======profit_province========");
console.log(profit_province);
console.log("sales_total:"+sales_total);
// console.log([sales_province,profit_province])
var myChart = echarts.init(document.getElementById('data_province'));
var mapName = 'china'
var geoCoordMap = {};
/*获取地图数据*/
myChart.showLoading();
var mapFeatures = echarts.getMap(mapName).geoJson.features;
myChart.hideLoading();
mapFeatures.forEach(function (v) {
// 地区名称
var name = v.properties.name;
// 地区经纬度
geoCoordMap[name] = v.properties.cp;
});
var max = 480,
min = 9; // todo
var maxSize4Pin = 100,
minSize4Pin = 20;
var convertData = function (data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = geoCoordMap[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value),
});
}
}
return res;
};
option = {
// backgroundColor: '#013954',
tooltip: {
padding: 0,
enterable: true,
transitionDuration: 1,
textStyle: {
color: '#000',
decoration: 'none',
},
formatter: function (params) {
var tipHtml = '';
tipHtml = '<div style="width:180px;height:120px;background:rgba(22,80,158,0.8);border:1px solid rgba(7,166,255,0.7)">'
+ '<div style="width:90%;height:30px;line-height:30px;border-bottom:2px solid rgba(7,166,255,0.7);padding:0 10px">' + '<i style="display:inline-block;width:8px;height:8px;background:#16d6ff;border-radius:30px;">' + '</i>'
+ '<span style="margin-left:10px;color:#fff;font-size:16px;">' + params.name + '</span>' + '</div>'
+ '<div style="padding:10px">'
+ '<p style="color:#fff;font-size:12px;">' + '<i style="display:inline-block;width:10px;height:10px;background:#16d6ff;border-radius:40px;margin:0 8px">' + '</i>'
+ '销售额:' + '<span style="color:#11ee7d;margin:0 6px;">' + sales_province[params.dataIndex].value + '</span>' + '元' + '</p>'
+ '<p style="color:#fff;font-size:12px;">' + '<i style="display:inline-block;width:10px;height:10px;background:#16d6ff;border-radius:40px;margin:0 8px">' + '</i>'
+ '利润:' + '<span style="color:#f48225;margin:0 6px;">' + profit_province[params.dataIndex].value + '</span>' + '元' + '</p>'
+ '</div>' + '</div>';
return tipHtml;
}
},
visualMap: {
show: true,
min: 0,
max: 300000,
left: '10%',
top: 'bottom',
calculable: true,
seriesIndex: [1],
inRange: {
color: ['#04387b', '#467bc0'] // 蓝绿
},
textStyle: {
color: '#fff'
}
},
geo: {
show: true,
map: mapName,
label: {
normal: {
show: false
},
emphasis: {
show: false,
}
},
roam: false,
itemStyle: {
normal: {
areaColor: '#023677',
borderColor: '#1180c7',
},
emphasis: {
areaColor: '#4499d0',
}
}
},
series: [{
name: '散点',
type: 'scatter',
coordinateSystem: 'geo',
data: convertData(profit_province),
symbolSize: function (val) {
return val[2] / 8000;
},
label: {
normal: {
formatter: '{b}',
position: 'right',
show: true
},
emphasis: {
show: true
}
},
// animation: false,
itemStyle: {
normal: {
color: '#fff'
}
}
},
{
type: 'map',
map: mapName,
geoIndex: 0,
aspectScale: 0.5, //长宽比
showLegendSymbol: false, // 存在legend时显示
label: {
normal: {
show: true
},
emphasis: {
show: false,
textStyle: {
color: '#fff'
}
}
},
roam: true,
itemStyle: {
normal: {
areaColor: '#031525',
borderColor: '#FFFFFF',
},
emphasis: {
areaColor: '#2B91B7'
}
},
animation: false,
data: sales_province
},
{
name: '点',
type: 'scatter',
coordinateSystem: 'geo',
zlevel: 6,
},
{
name: 'Top 5',
type: 'effectScatter',
// animation: false,
coordinateSystem: 'geo',
data: convertData(profit_province),
symbolSize: function (val) {
return val[2] / 8000;
},
showEffectOn: 'render',
rippleEffect: { //涟漪特效
period: 4, //动画时间,值越小速度越快
brushType: 'stroke', //波纹绘制方式 stroke, fill
scale: 4 //波纹圆环最大限制,值越大波纹越大
},
hoverAnimation: true,
label: {
normal: {
formatter: '{b}',
position: 'left',
show: false
}
},
itemStyle: {
normal: {
color: 'yellow',
shadowBlur: 10,
shadowColor: 'yellow'
}
},
zlevel: 1
},
]
};
myChart.setOption(option)
var currentIndex = -1;
setInterval(function () {
var dataLen = option.series[0].data.length;
// 取消之前高亮的图形
myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: currentIndex
});
currentIndex = (currentIndex + 1) % dataLen;
// 高亮当前图形
myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: currentIndex
});
// 显示 tooltip
myChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: currentIndex
});
}, 1000);
//为id为title1的div添加文字
var title1 = document.getElementById("title1");
title1.innerHTML = "2023年总销售额是<b style='color:#f48225;font-size:24px'>&nbsp;" + sales_total + "&nbsp;</b>万元" +
"<br/><br/>" + "审图号:<b style='color:#ffffff;font-size:14px'>&nbsp;GS(2023)2767号&nbsp;</b>";
title1.style.color = "#467bc0";
title1.style.fontSize = "14px";
title1.style.fontWeight = "bold"
})

View File

@@ -0,0 +1,153 @@
// 发送 AJAX 请求获取数据
// 这段代码的目的是从指定的 API 端点获取销售区域数据,并将这些数据分解为三个独立的数组:
// region区域名称、sales销售额和 profit利润
fetch('http://127.0.0.1:5000/api/sales_region')
.then(response => response.json())
.then(data => {
var region=[];
var sales=[];
var profit=[];
data.forEach(item => {
region.push(item.region);
sales.push(parseFloat(item.sales));
profit.push(parseFloat(item.profit));
});
console.log("======sales_region======")
console.log(region);
console.log(sales);
console.log(profit);
// console.log([region,sales,profit])
var myChart = echarts.init(document.getElementById('data_region'));
var option = {
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
},
textStyle:{
color:'#0099FF',
fontSize:10,
}
},
grid: {
left: '2%',
right: '4%',
bottom: '14%',
top:'16%',
containLabel: true
},
legend: {
data: ['销售额', '利润'],
right: 10,
top:12,
textStyle: {
color: "#fff",
fontSize:10,
},
itemWidth: 12,
itemHeight: 10,
// itemGap: 35
},
xAxis: {
type: 'category',
data: region,
axisLine: {
lineStyle: {
color: 'white'
}
},
axisLabel:{
fontSize:8,
color:'#2094CA',
},
},
yAxis: {
type: 'value',
axisLine: {
show: true,
lineStyle: {
color: 'white'
}
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,0.3)'
}
},
axisLabel:{
fontSize:8,
color:'#2094CA',
},
},
series: [{
name: '销售额',
type: 'bar',
barWidth: '15%',
label:{show:false},
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#fccb05'
}, {
offset: 1,
color: '#f5804d'
}]),
barBorderRadius: 12,
},
},
data:sales,
},
{
name: '利润',
type: 'bar',
barWidth: '15%',
label:{show:false},
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#8bd46e'
}, {
offset: 1,
color: '#09bcb7'
}]),
barBorderRadius: 11,
}
},
data: profit,
}]
};
var currentIndex = -1;
setInterval(function () {
var dataLen = option.series[0].data.length;
// 取消之前高亮的图形
myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: currentIndex
});
currentIndex = (currentIndex + 1) % dataLen;
// 高亮当前图形
myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: currentIndex
});
// 显示 tooltip
myChart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: currentIndex
});
}, 2000);
myChart.setOption(option)
})

View File

@@ -0,0 +1,32 @@
function total_control(){
var title_index=0;
var time_div=document.getElementById("currentTime");
function start(){
function getTimeString(){
var time=new Date();
var hour=time.getHours();
var minute=time.getMinutes();
var second=time.getSeconds();
var year=time.getFullYear();
var month=time.getMonth()+1;
var day=time.getDate();
var week=time.getDay();
var weeks=["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
// console.log(time)
hour=hour<10?'0'+hour:hour;
minute=minute<10?'0'+minute:minute;
second=second<10?'0'+second:second;
day=day<10?'0'+day:day;
var currentweek=weeks[week];
return year+'年'+month+'月'+day+'日'+' '+currentweek+' '+hour+':'+minute+':'+second;
}
time_div.innerText=getTimeString();
}
setInterval(start);
window.onload=function(){
}
window.onresize=function(){
location.reload(true);
}
};