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

- 新建前端项目配置文件和样式文件
- 创建后端 Flask 应用和数据库连接管理器
- 定义多个 API 路由以获取销售数据
This commit is contained in:
dev_xulongjin 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 it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,57 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-05-13T02:49:05.718408Z",
"start_time": "2025-05-13T02:49:05.678953Z"
}
},
"source": [
"from pyecharts.charts import Bar\n",
"from pyecharts import options as opts\n",
"from pyecharts.globals import ThemeType"
],
"outputs": [],
"execution_count": 1
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"bar = (\n",
" Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))\n",
" .add_xaxis(['哲学','历史','教育','科技','文学','经济'])\n",
" .add_yaxis('商家 A',[25,20,36,40,75,90])\n",
")"
],
"id": "e254f05719aaae60"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,122 @@
# 1.导入必要的库和模块
from flask import Flask, jsonify, Blueprint
import pymysql
from flask_cors import CORS
from contextlib import contextmanager
from flask.json import JSONEncoder
from decimal import Decimal
# 2.创建 Flask 应用并配置
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False # 重要,让 JSON 数据按原字符编码输出,不转成 ASCII
app.config['JSONIFY_MIMETYPE'] = 'application/json; charset=utf-8'
CORS(app) # 解决跨域问题
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj) # 或 str(obj)
return super().default(obj)
app.json_encoder = CustomJSONEncoder
# 3.数据库连接上下文管理器
@contextmanager
def get_db_connection():
try:
connection = pymysql.connect(
host='hadoop102',
user='root',
port=3306,
password='123456',
database='marketing',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
yield connection
except Exception as e:
print(f"Error: {e}")
finally:
if connection:
connection.close()
# 4.创建蓝图
data_bp = Blueprint('data', __name__)
# 5.定义API路由
# sales_month的数据接口
@data_bp.route('/api/sales_month', methods=['GET'])
def sales_month():
with get_db_connection() as connection:
with connection.cursor() as cursor:
sql = "SELECT * FROM sales_month"
cursor.execute(sql)
results = cursor.fetchall()
return jsonify(results)
# sales_region的数据接口
@data_bp.route('/api/sales_region', methods=['GET'])
def sales_region():
with get_db_connection() as connection:
with connection.cursor() as cursor:
sql = "SELECT * FROM sales_region"
cursor.execute(sql)
results = cursor.fetchall()
return jsonify(results)
# sales_manager的数据接口
@data_bp.route('/api/sales_manager', methods=['GET'])
def sales_manager():
with get_db_connection() as connection:
with connection.cursor() as cursor:
sql = "SELECT * FROM sales_manager"
cursor.execute(sql)
results = cursor.fetchall()
return jsonify(results)
# sales_province的数据接口
@data_bp.route('/api/sales_province', methods=['GET'])
def sales_province():
with get_db_connection() as connection:
with connection.cursor() as cursor:
sql = "SELECT * FROM sales_province"
cursor.execute(sql)
results = cursor.fetchall()
return jsonify(results)
# sales_product的数据接口
@data_bp.route('/api/sales_product', methods=['GET'])
def combine_data():
result = []
with get_db_connection() as conn:
cursor = conn.cursor()
# 直接使用 JOIN 语句查询关联数据
sql = """
SELECT mc.name AS main_name, mc.value, sc.name AS sub_name, sc.value
FROM main_categories mc
JOIN sub_categories sc ON mc.id = sc.main_category_id
"""
cursor.execute(sql)
rows = cursor.fetchall()
category_dict = {}
for row in rows:
main_name, main_value, sub_name, sub_value = row.values()
if main_name not in category_dict:
category_dict[main_name] = {
"name": main_name,
"value": main_value,
"children": []
}
category_dict[main_name]["children"].append({
"name": sub_name,
"value": sub_value
})
result = list(category_dict.values())
return jsonify(result)
# 6.注册蓝图并运行应用
app.register_blueprint(data_bp)
if __name__ == '__main__':
app.run(debug=True)

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>网店运营大屏</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.aptana.ide.core.unifiedBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.aptana.projects.webnature</nature>
</natures>
<filteredResources>
<filter>
<id>1596432197735</id>
<name></name>
<type>26</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-name-matches-false-false-node_modules</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@ -0,0 +1,152 @@
@charset "utf-8";
/********** Global **********/
/*
*常用背景色 #0f1c30 #0b0f34 (6,64,102) (29,45,57) (7,33,58) (8,13,28) (15,43,36)
*/
html, body {
width:100%;
height:100%;
min-height:635px;
font-family:"microsoft yahei", arial, sans-serif;
background-color:#152a59;
background-repeat:no-repeat;
background-position:center;
background-size:100% 100%;
overflow-x:hidden;
overflow-y:auto;
}
body.bg06 {background-image:url("../img/bg06.png");}
.header {
margin:0 auto;
width:100%;
height:55px;
max-width:1920px;
background:url("../img/header-left.png") left center no-repeat, url("../img/header-right.png") right center no-repeat;
background-size:43% 100%, 43% 100%;
overflow:hidden;
}
.header h3 {
margin:0;
padding:0;
line-height:60px;
text-align:center;
font-size:24px;
color:#5dc2fe;
}
#currentTime {
position:absolute;
height:100%;
width:35%;
line-height:25px;
top:2%;
left:75%;
/*text-align: center;*/
font-size:10px;
color:#1890FF;
}
@media (max-width: 1199px) {
.header {
background:url("../img/header-left.png") left bottom no-repeat, url("../img/header-right.png") right bottom no-repeat;
background-size:100%, 100%;
}
.header h3 {line-height:48px;}
}
.wrapper {position:absolute;top:70px;bottom:0;left:0;right:0;min-height:555px;}
.container-fluid {height:100%;min-height:100%;}
.row {margin-left:-7px;margin-right:-8px;}
.row>div {padding-left:7px;padding-right:8px;}
.xpanel-wrapper {padding-bottom:15px;box-sizing:border-box;}
.xpanel-wrapper-1 {height:100%;}
.xpanel-wrapper-1-2 {height:50%;}
.xpanel-wrapper-1-3 {height:50%;}
.xpanel-wrapper-2-3 {height:50%;}
.xpanel {
position: relative;
padding:15px;
height:100%;
min-height:170px;
background:url("../img/panel.png") center no-repeat;
background-size:100% 100%;
box-sizing:border-box;
}
.title {
position: absolute;
padding-left:24px;
height:36px;
width: 90%;
line-height:36px;
font-size:15px;
font-weight:normal;
color:#00C6FB;
background-image:url("../img/title-bg.png");
background-repeat:no-repeat;
background-size:100% 100%;
margin: 0;
}
.center_title {
position: absolute;
padding-left:24px;
height:36px;
width: 90%;
line-height:36px;
font-size:15px;
font-weight:normal;
color:#00C6FB;
background-image:url("../img/title-bg.png");
background-repeat:no-repeat;
background-size:100% 100%;
margin: 0;
top:2%;
}
.title1 {
position: absolute;
padding-left:15px;
height:30px;
width: 90%;
line-height:15px;
font-size:15px;
font-weight:normal;
color:#FFFFFF;
background:none;
background-repeat:no-repeat;
background-size:100% 100%;
margin: 0;
}
/* tool */
.fill-h {height:100%;min-height:100%;}
.no-margin {margin:0 !important;}
.no-padding {padding:0 !important;}
.no-bg {background:none !important;}
.no-border {border:0 !important;}
.left_right_div {
position: absolute;
height:79%;
width: 93%;
top:20%;
padding: 0px 0px 0px 0px;
background:none;
margin: 0;
}
.center_div {
position: absolute;
height:88%;
width: 100%;
top:8%;
padding: 0;
background:none;
margin: 0;
}
.center_div1 {
position: absolute;
height:100%;
width: 100%;
top:5%;
padding: 0;
background:none;
margin: 0;
}
/* scrollbar */
::-webkit-scrollbar {width:0;height:0;}
::-webkit-scrollbar-track {background-color:transparent;}
::-webkit-scrollbar-thumb {border-radius:5px;background-color:rgba(0, 0, 0, 0.3);}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
sales_manager,sales,profit
郝杰,502667.277,55300.637
江奕健,179421.48,11725
姜伟,222300.092,19919.872
王倩倩,691845.854,92687.014
杨洪光,761143.054,96180.574
张怡莲,558296.522,93976.162
1 sales_manager sales profit
2 郝杰 502667.277 55300.637
3 江奕健 179421.48 11725
4 姜伟 222300.092 19919.872
5 王倩倩 691845.854 92687.014
6 杨洪光 761143.054 96180.574
7 张怡莲 558296.522 93976.162

View File

@ -0,0 +1,13 @@
month,sales,profit
1月,226678.83,37233.77
2月,109855.158,19834.458
3月,167173.727,18748.247
4月,96984.692,16501.352
5月,232199.107,23984.947
6月,339729.698,39157.118
7月,140050.918,11800.698
8月,356128.507,66818.087
9月,322759.367,24133.207
10月,289269.169,33635.329
11月,342142.15,42611.87
12月,292702.956,35330.176
1 month sales profit
2 1月 226678.83 37233.77
3 2月 109855.158 19834.458
4 3月 167173.727 18748.247
5 4月 96984.692 16501.352
6 5月 232199.107 23984.947
7 6月 339729.698 39157.118
8 7月 140050.918 11800.698
9 8月 356128.507 66818.087
10 9月 322759.367 24133.207
11 10月 289269.169 33635.329
12 11月 342142.15 42611.87
13 12月 292702.956 35330.176

View File

@ -0,0 +1,65 @@
[
{
"name": "办公用品",
"value": 949270.224,
"children": [{
"name": "标签",
"value": 18936.54
}, {
"name": "美术",
"value": 33038.096
}, {
"name": "器具",
"value": 449744.764
}, {
"name": "收纳具",
"value": 214582.2
}, {
"name": "系固件",
"value": 20045.48
}, {
"name": "信封",
"value": 51941.96
}, {
"name": "用品",
"value": 47070.128
}, {
"name": "纸张",
"value": 51413.74
},{
"name": "装订机",
"value": 62497.316
}]
}, {
"name": "技术",
"value": 937985.552,
"children": [{
"name": "电话",
"value": 330951.32
}, {
"name": "复印机",
"value": 347513.264
}, {
"name": "配件",
"value": 150562.664
}, {
"name": "设备",
"value": 108958.304
}]
}, {
"name": "家具",
"value": 1028418.503,
"children": [{
"name": "书架",
"value": 366969.54
}, {
"name": "椅子",
"value": 409393.054
}, {
"name": "用具",
"value": 83848.996
}, {
"name": "桌子",
"value": 168206.913
}]
}]

View File

@ -0,0 +1,31 @@
province,sales,profit
安徽,113742.734,32222.834
北京,76154.54,19854.24
福建,102315.08,32774.7
甘肃,47267.444,-13210.036
广东,254769.725,58887.605
广西,48349.532,14036.932
贵州,15893.136,287.616
海南,10995.88,4132.94
河北,227145.8,43675.38
河南,79220.54,17722.6
黑龙江,273877.632,66982.132
湖北,135006.9,-33492.34
湖南,163503.277,31399.277
吉林,76539.617,17712.317
江苏,128183.776,-16801.764
江西,29987.86,7944.72
辽宁,152250.028,-29393.812
内蒙古,59787.252,-15047.368
宁夏,12685.988,-900.592
山东,192400.46,49812.56
山西,99782.9,25386.9
陕西,102767.168,21152.628
上海,99893.584,17668.784
四川,60567.584,-9514.876
天津,95426.03,20107.01
西藏,1144.92,400.26
新疆,16700.88,4683
云南,79432.052,16937.032
浙江,94619.56,-27441.26
重庆,65262.4,11809.84
1 province sales profit
2 安徽 113742.734 32222.834
3 北京 76154.54 19854.24
4 福建 102315.08 32774.7
5 甘肃 47267.444 -13210.036
6 广东 254769.725 58887.605
7 广西 48349.532 14036.932
8 贵州 15893.136 287.616
9 海南 10995.88 4132.94
10 河北 227145.8 43675.38
11 河南 79220.54 17722.6
12 黑龙江 273877.632 66982.132
13 湖北 135006.9 -33492.34
14 湖南 163503.277 31399.277
15 吉林 76539.617 17712.317
16 江苏 128183.776 -16801.764
17 江西 29987.86 7944.72
18 辽宁 152250.028 -29393.812
19 内蒙古 59787.252 -15047.368
20 宁夏 12685.988 -900.592
21 山东 192400.46 49812.56
22 山西 99782.9 25386.9
23 陕西 102767.168 21152.628
24 上海 99893.584 17668.784
25 四川 60567.584 -9514.876
26 天津 95426.03 20107.01
27 西藏 1144.92 400.26
28 新疆 16700.88 4683
29 云南 79432.052 16937.032
30 浙江 94619.56 -27441.26
31 重庆 65262.4 11809.84

View File

@ -0,0 +1,7 @@
region,sales,profit
东北,502667.277,55300.637
华北,558296.522,93976.162
华东,761143.054,96180.574
西北,179421.48,11725
西南,222300.092,19919.872
中南,691845.854,92687.014
1 region sales profit
2 东北 502667.277 55300.637
3 华北 558296.522 93976.162
4 华东 761143.054 96180.574
5 西北 179421.48 11725
6 西南 222300.092 19919.872
7 中南 691845.854 92687.014

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<title>网店运营销售数据大屏</title>
<link rel="stylesheet" href="css/bootstrap.min.css" />
<link rel="stylesheet" href="css/app.css" />
<script src="js/echarts.min.js"></script>
<script src="js/d3.min.js"></script>
<script src="js/china.js"></script>
</head>
<body class="bg06">
<header class="header">
<div id='currentTime'></div>
<h3>网店运营销售大屏</h3>
</header>
<div class="wrapper">
<div class="container-fluid">
<div class="row fill-h">
<div class="col-lg-3 fill-h">
<div class="xpanel-wrapper xpanel-wrapper-1-2">
<div class="xpanel">
<div class="title">1-12月各月销售和利润情况</div>
<div class="left_right_div" id="data_month"></div>
</div>
</div>
<div class="xpanel-wrapper xpanel-wrapper-1-2">
<div class="xpanel">
<div class="title">各产品销售情况</div>
<div class="left_right_div" id="data_product"></div>
</div>
</div>
</div>
<div class="col-lg-6 fill-h">
<div class="xpanel-wrapper xpanel-wrapper-1">
<div class="xpanel no-padding no-bg">
<div class="center_title">各省份销售和利润情况</div>
<div class="center_div">
<div class="title1" id="title1"></div>
<div class="center_div1" id="data_province"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 fill-h">
<div class="xpanel-wrapper xpanel-wrapper-2-3">
<div class="xpanel">
<div class="title">各地区销售和利润情况</div>
<div class="left_right_div" id="data_region"></div>
</div>
</div>
<div class="xpanel-wrapper xpanel-wrapper-1-3">
<div class="xpanel">
<div class="title">各产品经理销售和利润情况</div>
<div class="left_right_div" id="data_manager"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="js/totalControl.js"></script>
<script>
total_control()
</script>
<script src="js/sales_month.js"></script>
<script src="js/sales_product.js"></script>
<script src="js/sales_province.js"></script>
<script src="js/sales_region.js"></script>
<script src="js/sales_manager.js"></script>
</body>
</html>

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

View File

@ -0,0 +1,183 @@
/*
Navicat Premium Data Transfer
Source Server : localhost_3306
Source Server Type : MySQL
Source Server Version : 80020
Source Host : localhost:3306
Source Schema : marketing
Target Server Type : MySQL
Target Server Version : 80020
File Encoding : 65001
Date: 20/04/2025 00:05:44
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for main_categories
-- ----------------------------
DROP TABLE IF EXISTS `main_categories`;
CREATE TABLE `main_categories` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`value` decimal(10, 3) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of main_categories
-- ----------------------------
INSERT INTO `main_categories` VALUES (1, '办公用品', 949270.224);
INSERT INTO `main_categories` VALUES (2, '技术', 937985.552);
INSERT INTO `main_categories` VALUES (3, '家具', 1028418.503);
-- ----------------------------
-- Table structure for sales_manager
-- ----------------------------
DROP TABLE IF EXISTS `sales_manager`;
CREATE TABLE `sales_manager` (
`sales_manager` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`sales` decimal(10, 3) NULL DEFAULT NULL,
`profit` decimal(10, 3) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sales_manager
-- ----------------------------
INSERT INTO `sales_manager` VALUES ('郝杰', 502667.277, 55300.637);
INSERT INTO `sales_manager` VALUES ('江奕健', 179421.480, 11725.000);
INSERT INTO `sales_manager` VALUES ('姜伟', 222300.092, 19919.872);
INSERT INTO `sales_manager` VALUES ('王倩倩', 691845.854, 92687.014);
INSERT INTO `sales_manager` VALUES ('杨洪光', 761143.054, 96180.574);
INSERT INTO `sales_manager` VALUES ('张怡莲', 558296.522, 93976.162);
-- ----------------------------
-- Table structure for sales_month
-- ----------------------------
DROP TABLE IF EXISTS `sales_month`;
CREATE TABLE `sales_month` (
`month` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`sales` decimal(10, 3) NULL DEFAULT NULL,
`profit` decimal(10, 3) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sales_month
-- ----------------------------
INSERT INTO `sales_month` VALUES ('1月', 226678.830, 37233.770);
INSERT INTO `sales_month` VALUES ('2月', 109855.158, 19834.458);
INSERT INTO `sales_month` VALUES ('3月', 167173.727, 18748.247);
INSERT INTO `sales_month` VALUES ('4月', 96984.692, 16501.352);
INSERT INTO `sales_month` VALUES ('5月', 232199.107, 23984.947);
INSERT INTO `sales_month` VALUES ('6月', 339729.698, 39157.118);
INSERT INTO `sales_month` VALUES ('7月', 140050.918, 11800.698);
INSERT INTO `sales_month` VALUES ('8月', 356128.507, 66818.087);
INSERT INTO `sales_month` VALUES ('9月', 322759.367, 24133.207);
INSERT INTO `sales_month` VALUES ('10月', 289269.169, 33635.329);
INSERT INTO `sales_month` VALUES ('11月', 342142.150, 42611.870);
INSERT INTO `sales_month` VALUES ('12月', 292702.956, 35330.176);
-- ----------------------------
-- Table structure for sales_province
-- ----------------------------
DROP TABLE IF EXISTS `sales_province`;
CREATE TABLE `sales_province` (
`province` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`sales` decimal(10, 3) NULL DEFAULT NULL,
`profit` decimal(10, 3) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sales_province
-- ----------------------------
INSERT INTO `sales_province` VALUES ('安徽', 113742.734, 32222.834);
INSERT INTO `sales_province` VALUES ('北京', 76154.540, 19854.240);
INSERT INTO `sales_province` VALUES ('福建', 102315.080, 32774.700);
INSERT INTO `sales_province` VALUES ('甘肃', 47267.444, -13210.036);
INSERT INTO `sales_province` VALUES ('广东', 254769.725, 58887.605);
INSERT INTO `sales_province` VALUES ('广西', 48349.532, 14036.932);
INSERT INTO `sales_province` VALUES ('贵州', 15893.136, 287.616);
INSERT INTO `sales_province` VALUES ('海南', 10995.880, 4132.940);
INSERT INTO `sales_province` VALUES ('河北', 227145.800, 43675.380);
INSERT INTO `sales_province` VALUES ('河南', 79220.540, 17722.600);
INSERT INTO `sales_province` VALUES ('黑龙江', 273877.632, 66982.132);
INSERT INTO `sales_province` VALUES ('湖北', 135006.900, -33492.340);
INSERT INTO `sales_province` VALUES ('湖南', 163503.277, 31399.277);
INSERT INTO `sales_province` VALUES ('吉林', 76539.617, 17712.317);
INSERT INTO `sales_province` VALUES ('江苏', 128183.776, -16801.764);
INSERT INTO `sales_province` VALUES ('江西', 29987.860, 7944.720);
INSERT INTO `sales_province` VALUES ('辽宁', 152250.028, -29393.812);
INSERT INTO `sales_province` VALUES ('内蒙古', 59787.252, -15047.368);
INSERT INTO `sales_province` VALUES ('宁夏', 12685.988, -900.592);
INSERT INTO `sales_province` VALUES ('山东', 192400.460, 49812.560);
INSERT INTO `sales_province` VALUES ('山西', 99782.900, 25386.900);
INSERT INTO `sales_province` VALUES ('陕西', 102767.168, 21152.628);
INSERT INTO `sales_province` VALUES ('上海', 99893.584, 17668.784);
INSERT INTO `sales_province` VALUES ('四川', 60567.584, -9514.876);
INSERT INTO `sales_province` VALUES ('天津', 95426.030, 20107.010);
INSERT INTO `sales_province` VALUES ('西藏', 1144.920, 400.260);
INSERT INTO `sales_province` VALUES ('新疆', 16700.880, 4683.000);
INSERT INTO `sales_province` VALUES ('云南', 79432.052, 16937.032);
INSERT INTO `sales_province` VALUES ('浙江', 94619.560, -27441.260);
INSERT INTO `sales_province` VALUES ('重庆', 65262.400, 11809.840);
-- ----------------------------
-- Table structure for sales_region
-- ----------------------------
DROP TABLE IF EXISTS `sales_region`;
CREATE TABLE `sales_region` (
`region` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`sales` decimal(10, 3) NULL DEFAULT NULL,
`profit` decimal(10, 3) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sales_region
-- ----------------------------
INSERT INTO `sales_region` VALUES ('东北', 502667.277, 55300.637);
INSERT INTO `sales_region` VALUES ('华北', 558296.522, 93976.162);
INSERT INTO `sales_region` VALUES ('华东', 761143.054, 96180.574);
INSERT INTO `sales_region` VALUES ('西北', 179421.480, 11725.000);
INSERT INTO `sales_region` VALUES ('西南', 222300.092, 19919.872);
INSERT INTO `sales_region` VALUES ('中南', 691845.854, 92687.014);
-- ----------------------------
-- Table structure for sub_categories
-- ----------------------------
DROP TABLE IF EXISTS `sub_categories`;
CREATE TABLE `sub_categories` (
`id` int NOT NULL AUTO_INCREMENT,
`main_category_id` int NULL DEFAULT NULL,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`value` decimal(10, 3) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `main_category_id`(`main_category_id`) USING BTREE,
CONSTRAINT `sub_categories_ibfk_1` FOREIGN KEY (`main_category_id`) REFERENCES `main_categories` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sub_categories
-- ----------------------------
INSERT INTO `sub_categories` VALUES (1, 1, '标签', 18936.540);
INSERT INTO `sub_categories` VALUES (2, 1, '美术', 33038.096);
INSERT INTO `sub_categories` VALUES (3, 1, '器具', 449744.764);
INSERT INTO `sub_categories` VALUES (4, 1, '收纳具', 214582.200);
INSERT INTO `sub_categories` VALUES (5, 1, '系固件', 20045.480);
INSERT INTO `sub_categories` VALUES (6, 1, '信封', 51941.960);
INSERT INTO `sub_categories` VALUES (7, 1, '用品', 47070.128);
INSERT INTO `sub_categories` VALUES (8, 1, '纸张', 51413.740);
INSERT INTO `sub_categories` VALUES (9, 1, '装订机', 62497.316);
INSERT INTO `sub_categories` VALUES (10, 2, '电话', 330951.320);
INSERT INTO `sub_categories` VALUES (11, 2, '复印机', 347513.264);
INSERT INTO `sub_categories` VALUES (12, 2, '配件', 150562.664);
INSERT INTO `sub_categories` VALUES (13, 2, '设备', 108958.304);
INSERT INTO `sub_categories` VALUES (14, 3, '书架', 366969.540);
INSERT INTO `sub_categories` VALUES (15, 3, '椅子', 409393.054);
INSERT INTO `sub_categories` VALUES (16, 3, '用具', 83848.996);
INSERT INTO `sub_categories` VALUES (17, 3, '桌子', 168206.913);
SET FOREIGN_KEY_CHECKS = 1;