Python Flask+ECharts+MySQL数据大屏实战 📅 发布时间:2026/9/17 18:29:38 👁 浏览次数: 简介本资源是一份面向高校计算机专业学生的数据可视化课程结课作业报告聚焦用电量分布分析场景完整呈现从数据库设计、后端服务搭建到前端图表渲染的全流程实践。报告基于虚构但具代表性的日用电量数据集系统运用MySQL存储数据、Flask构建轻量Web服务、ECharts实现动态交互式图表并涵盖中文乱码修复、图表不显示等典型问题的排错思路与解决方案。资源为单个543KB PDF文件内容结构清晰含项目摘要、数据库表设计epower表含time/powers双字段、Flask路由与模板渲染逻辑、前端HTML/JS集成方法及问题总结等核心模块适合初学者理解Web数据可视化项目整体架构与技术协同机制。目前已有136人学习下载可作为课程实践参考、毕设选题范例或PythonWeb可视化入门学习材料。1. 这份《数据可视化结课作业.pdf》不是交差文档而是用 Python Flask ECharts MySQL 搭建可运行、可验证、可扩展的轻量级数据大屏的完整实践路径很多同学拿到“数据可视化结课作业”第一反应是找模板、改颜色、导出截图——但真正拉开差距的是从 PDF 里反向还原出一个能python app.py启动、能连真实 MySQL 表、能动态刷新 ECharts 图表、能被浏览器访问的 Web 应用。这份 PDF 的核心价值不在排版美观而在它隐含了一条工业级数据可视化最小闭环数据层MySQL→ 服务层Flask→ 渲染层ECharts→ 交互层HTML/JS。它面向的是刚学完 Python 基础、接触过 SQL 语法、但尚未把前后端串起来的高校学生或转行新人也适用于需要快速验证业务指标、不依赖商业 BI 工具的中小团队技术负责人。PDF 中每一页图表背后都对应着一个可调试的/api/chart1接口、一段可修改的 SQL 查询、一组可替换的 ECharts 配置项。接下来我们就从 PDF 的典型结构出发把静态 PDF 变成可执行、可调试、可部署的本地数据可视化系统。2. 用 Flask 构建最小 API 服务从 PDF 图表描述反推接口设计与路由映射一份合格的结课作业 PDF 通常包含「柱状图展示各省份销售额」「折线图显示月度用户增长」「饼图分析产品类别占比」等描述。这些文字不是装饰而是接口设计说明书。我们不从零写路由而是按 PDF 页面顺序逐页提取图表语义生成对应 Flask 路由和数据查询逻辑。2.1 根据 PDF 图表标题定义 RESTful 接口路径与返回结构假设 PDF 第 3 页标题为“2024 年 Q1 各区域订单量对比柱状图”这直接对应一个 GET 接口# app.py from flask import Flask, jsonify import pymysql app Flask(__name__) app.route(/api/region_orders_q1, methods[GET]) def get_region_orders_q1(): conn pymysql.connect( hostlocalhost, userroot, passwordyour_password, databasesales_db, charsetutf8mb4 ) cursor conn.cursor(pymysql.cursors.DictCursor) # 注意SQL 必须明确限定时间范围避免全表扫描 sql SELECT region AS name, SUM(order_count) AS value FROM orders WHERE order_date BETWEEN 2024-01-01 AND 2024-03-31 GROUP BY region ORDER BY value DESC cursor.execute(sql) data cursor.fetchall() cursor.close() conn.close() return jsonify({ code: 200, data: data, msg: success })提示pymysql.cursors.DictCursor确保返回字典列表如[{name:华东,value:1250}]ECharts 直接消费无需额外键名映射。若 PDF 中未注明时间范围按教学惯例默认使用2024-Q1或最近三个月并在代码注释中明确标注方便后续替换。2.2 多图表共用同一数据库连接池避免频繁创建销毁连接每次请求都新建 MySQL 连接会迅速耗尽资源。应使用pymysql的连接池机制在应用启动时初始化# db_pool.py import pymysql from DBUtils.PooledDB import PooledDB pool PooledDB( creatorpymysql, maxconnections10, # 最大连接数 mincached2, # 初始化时创建的空闲连接数 hostlocalhost, userroot, passwordyour_password, databasesales_db, charsetutf8mb4, autocommitTrue ) def get_conn(): return pool.connection()然后在路由中调用# app.py 中修改 from db_pool import get_conn app.route(/api/region_orders_q1, methods[GET]) def get_region_orders_q1(): conn get_conn() # 从池中获取连接 cursor conn.cursor(pymysql.cursors.DictCursor) try: cursor.execute(SELECT region AS name, SUM(order_count) AS value ...) data cursor.fetchall() return jsonify({code: 200, data: data}) finally: cursor.close() conn.close() # 归还连接到池非销毁参数推荐值说明maxconnections10–20学生作业场景并发低10 足够企业测试环境建议 30mincached2–5避免首次请求等待连接创建提升响应速度autocommitTrue必开结课作业无事务需求关闭 autocommit 需显式 commit易遗漏2.3 使用蓝图Blueprint组织多图表路由匹配 PDF 章节结构PDF 若分“销售分析”“用户行为”“产品结构”三章就应拆分为三个蓝图便于后期维护# blueprints/sales.py from flask import Blueprint, jsonify from db_pool import get_conn bp Blueprint(sales, __name__, url_prefix/api/sales) bp.route(/region_orders_q1, methods[GET]) def region_orders_q1(): # 同上查询逻辑 pass bp.route(/monthly_revenue, methods[GET]) def monthly_revenue(): # PDF 第 4 页2024 年各月营收折线图 pass# app.py 主文件 from blueprints.sales import bp as sales_bp from blueprints.user import bp as user_bp app.register_blueprint(sales_bp) app.register_blueprint(user_bp)这样PDF 的“第 3 页 →/api/sales/region_orders_q1”、“第 5 页 →/api/user/retention_rate”形成清晰映射增删图表只需操作对应蓝图文件不污染主应用。3. 用 ECharts 渲染 PDF 图表从静态截图还原可交互、可响应、可配置的 JavaScript 实现PDF 中的图表是最终呈现效果ECharts 是实现该效果的开源引擎。关键不是“画得像”而是“行为一致”鼠标悬停显示数值、点击下钻、缩放、自适应容器宽度。我们以 PDF 中最常见的三种图表为例给出可直接复制粘贴的初始化代码。3.1 柱状图严格遵循 PDF 的坐标轴标签、颜色、排序逻辑假设 PDF 柱状图横轴为省份纵轴为销售额且按“华东 华南 华北”降序排列。ECharts 配置必须与之完全一致// static/js/charts/region-bar.js function initRegionBarChart() { const chartDom document.getElementById(region-bar); const myChart echarts.init(chartDom); // 从后端获取数据注意key 必须与 Flask 返回字段一致 fetch(/api/sales/region_orders_q1) .then(res res.json()) .then(data { if (data.code ! 200) throw new Error(data.msg); // ECharts 要求 xAxis.data 和 series.data 一一对应且顺序决定渲染顺序 const categories data.data.map(item item.name); // [华东, 华南, 华北] const values data.data.map(item item.value); // [1250, 980, 760] const option { tooltip: { trigger: axis, formatter: {b}br/{c} 单 }, grid: { left: 3%, right: 4%, bottom: 3%, containLabel: true }, xAxis: { type: category, data: categories, axisLabel: { rotate: 0 } // PDF 中标签水平此处禁用旋转 }, yAxis: { type: value, name: 订单量, nameLocation: middle, nameGap: 30 }, series: [{ name: 订单量, type: bar, data: values, itemStyle: { color: function(params) { // 按 PDF 配色方案华东#5470C6华南#91CC75华北#FAC858 const colors [#5470C6, #91CC75, #FAC858]; return colors[params.dataIndex % colors.length]; } } }] }; myChart.setOption(option); }); } // 页面加载完成后初始化 document.addEventListener(DOMContentLoaded, initRegionBarChart);注意xAxis.data必须与后端返回的name字段顺序严格一致。若 PDF 中“华东”排第一而 SQL 查询未加ORDER BY value DESC则前端排序会破坏 PDF 原意。务必在 SQL 层保证顺序。3.2 折线图处理时间序列的 X 轴刻度与自动换行 tooltipPDF 中“月度用户增长”折线图X 轴为月份如 “2024-01”, “2024-02”常因标签过长导致重叠。ECharts 提供axisLabel.formatter解决xAxis: { type: category, data: [2024-01, 2024-02, 2024-03, 2024-04], axisLabel: { formatter: function(value) { // 将 2024-01 拆成两行显示适配窄屏 return value.split(-).join(\n); }, fontSize: 12 } }, tooltip: { // tooltip 内容过长时自动换行 formatter: function(params) { return ${params[0].name}br/ span styledisplay:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params[0].color}/span ${params[0].seriesName}${params[0].value} 人; } }3.3 饼图实现 PDF 中的占比标注与引导线样式PDF 饼图常要求“标签外移引导线百分比”ECharts 通过label和labelLine控制series: [{ name: 产品类别, type: pie, radius: [40%, 70%], // 内外半径形成环形图效果PDF 常用 avoidLabelOverlap: false, label: { show: true, position: outside, formatter: {b}: {d}%\n{c} 件, // {b}名称 {d}百分比 {c}数值 lineHeight: 20 }, labelLine: { show: true, length: 15, // 引导线长度 length2: 20, // 引导线末端到标签距离 smooth: true // 曲线引导线更美观 }, data: [ { value: 1200, name: 手机 }, { value: 850, name: 平板 }, { value: 620, name: 耳机 } ] }]提示formatter中的\n是关键它让“名称百分比”和“数值”分两行显示避免 PDF 中常见的标签拥挤问题。若 PDF 使用环形图radius必须设为数组若为普通饼图设为单值如60%。4. MySQL 数据库准备按 PDF 图表需求反向建表、填测试数据、验证查询逻辑PDF 不是数据源但它定义了数据结构。结课作业常见错误是“先做图再凑数”导致 SQL 查询无法返回预期字段。正确做法是根据 PDF 图表标题逆向设计表结构 → 插入符合业务逻辑的测试数据 → 执行路由中 SQL 验证结果。4.1 从 PDF 图表描述生成 DDL 创建表PDF 中“各省份销售额柱状图”隐含维度表regions和事实表sales-- 创建省份维度表确保中文字符支持 CREATE TABLE regions ( id INT PRIMARY KEY AUTO_INCREMENT, province VARCHAR(20) NOT NULL COMMENT 省份名称, region VARCHAR(10) NOT NULL COMMENT 大区华东/华南等, INDEX idx_province (province) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建销售事实表 CREATE TABLE sales ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id VARCHAR(32) NOT NULL, province VARCHAR(20) NOT NULL, amount DECIMAL(10,2) NOT NULL COMMENT 销售额, sale_date DATE NOT NULL, INDEX idx_province_date (province, sale_date), INDEX idx_date (sale_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意CHARSETutf8mb4是必须项否则插入“广东省”等中文可能报错INDEX为后续GROUP BY province和WHERE sale_date提速避免全表扫描。4.2 插入符合 PDF 图表比例的测试数据不能随机生成要模拟真实分布。例如 PDF 柱状图中“华东:华南:华北 ≈ 5:3:2”则插入数据按此比例-- 插入省份维度 INSERT INTO regions (province, region) VALUES (上海市, 华东), (江苏省, 华东), (浙江省, 华东), (广东省, 华南), (广西壮族自治区, 华南), (北京市, 华北), (河北省, 华北); -- 插入销售数据华东占 50%华南 30%华北 20% INSERT INTO sales (order_id, province, amount, sale_date) VALUES (ORD2024001, 上海市, 12500.00, 2024-01-15), (ORD2024002, 江苏省, 9800.00, 2024-02-10), (ORD2024003, 浙江省, 8200.00, 2024-03-05), (ORD2024004, 广东省, 6200.00, 2024-01-20), (ORD2024005, 广西壮族自治区, 4100.00, 2024-02-25), (ORD2024006, 北京市, 3800.00, 2024-01-30), (ORD2024007, 河北省, 2900.00, 2024-03-10);4.3 在 MySQL 命令行验证路由 SQL确保与 PDF 一致启动 Flask 前先在 MySQL 客户端执行路由中的 SQL检查字段名、数据类型、排序是否匹配-- 复制 app.py 中的 SQL 到 MySQL 命令行 SELECT province AS name, SUM(amount) AS value FROM sales WHERE sale_date BETWEEN 2024-01-01 AND 2024-03-31 GROUP BY province ORDER BY value DESC; -- 预期输出 -- ------------------- -- | name | value | -- ------------------- -- | 上海市 | 12500.00 | -- | 江苏省 | 9800.00 | -- | 浙江省 | 8200.00 | -- -------------------若结果为空检查sale_date格式是否为DATE类型若字段名为province但 ECharts 需要name必须在 SQL 中用AS name别名。所有字段名必须与 EChartsxAxis.data/series.data的取值路径完全一致。5. 本地运行与调试启动 Flask 服务、打开 HTML 页面、用浏览器开发者工具定位问题完成上述步骤后项目目录结构应为project/ ├── app.py # Flask 主程序 ├── db_pool.py # 数据库连接池 ├── requirements.txt ├── templates/ │ └── index.html # 包含 ECharts 容器的主页面 └── static/ ├── css/ │ └── style.css └── js/ ├── charts/ │ ├── region-bar.js # 柱状图 │ ├── monthly-line.js # 折线图 │ └── product-pie.js # 饼图 └── main.js # 初始化所有图表5.1 用 pip 安装依赖并启动服务# 创建虚拟环境推荐 python -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate # Windows # 安装核心依赖 pip install flask pymysql DBUtils echarts-python # 启动服务默认 http://localhost:5000 python app.py提示若报错ModuleNotFoundError: No module named pymysql确认已激活虚拟环境若 MySQL 连接失败检查db_pool.py中的host是否为localhost非127.0.0.1某些 MySQL 配置下有差异。5.2 在 index.html 中引入 ECharts 并挂载图表容器!-- templates/index.html -- !DOCTYPE html html head meta charsetUTF-8 title数据可视化结课作业/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script /head body h1销售数据分析大屏/h1 !-- 柱状图容器 -- div idregion-bar stylewidth: 800px; height: 400px;/div !-- 折线图容器 -- div idmonthly-line stylewidth: 800px; height: 400px;/div !-- 饼图容器 -- div idproduct-pie stylewidth: 800px; height: 400px;/div !-- 按顺序加载图表 JS -- script src{{ url_for(static, filenamejs/charts/region-bar.js) }}/script script src{{ url_for(static, filenamejs/charts/monthly-line.js) }}/script script src{{ url_for(static, filenamejs/charts/product-pie.js) }}/script /body /html5.3 使用浏览器开发者工具F12快速定位三类高频问题问题现象定位方法典型原因修复动作图表空白控制台报echarts is not defined切换到 Console 标签ECharts CDN 加载失败或路径错误检查script src...是否可访问或改用本地static/js/echarts.min.js图表显示但数据为空Network 中/api/xxx返回 500切换到 Network 标签点击对应请求Flask 路由中 SQL 语法错误或数据库连接失败查看 Flask 终端报错日志修正 SQL 或db_pool.py配置图表渲染错位、文字重叠切换到 Elements 标签检查容器 div 的style容器 width/height 为 0 或未设置在index.html中为每个#region-bar等容器显式设置stylewidth:800px;height:400px;注意ECharts 初始化必须在 DOM 元素存在后执行。document.addEventListener(DOMContentLoaded, ...)是安全写法若将 JS 放在head中需加defer属性否则getElementById返回 null。6. 进阶技巧用 Flask-WTF 防止 CSRF、用 Jinja2 模板变量注入动态标题、用 ECharts dataset 简化多图数据管理当 PDF 图表超过 5 张手动维护每个fetch请求和setOption易出错。此时应升级架构用标准化方式管理数据流。6.1 用 Jinja2 模板变量动态注入页面标题与图表元信息PDF 中每页有标题如“2024年Q1销售分析”不应硬编码在 HTML 中。改为从 Flask 传入# app.py app.route(/) def index(): return render_template(index.html, page_title2024年Q1销售分析, charts_config[ {id: region-bar, title: 各区域订单量}, {id: monthly-line, title: 月度营收趋势}, {id: product-pie, title: 产品类别占比} ])!-- templates/index.html -- title{{ page_title }}/title h1{{ page_title }}/h1 {% for chart in charts_config %} h2{{ chart.title }}/h2 div id{{ chart.id }} stylewidth: 800px; height: 400px;/div {% endfor %}这样PDF 更新标题时只需改app.py中page_title字符串无需触碰 HTML。6.2 用 ECharts dataset 实现“一源多图”避免重复 fetch多个图表可能基于同一数据集如所有图表都用sales表的 Q1 数据。传统做法是每个图表发一次/api/sales/q1效率低。改用dataset// 在 main.js 中统一获取数据 let globalDataset null; fetch(/api/sales/q1_all) .then(res res.json()) .then(data { globalDataset data.data; // [{province:上海,amount:12500,...}, ...] // 初始化所有图表 initRegionBarChart(globalDataset); initMonthlyLineChart(globalDataset); initProductPieChart(globalDataset); }); function initRegionBarChart(data) { const chart echarts.init(document.getElementById(region-bar)); chart.setOption({ dataset: { source: data }, // 直接传入数组 series: [{ type: bar, encode: { x: province, y: amount } }] }); }优势数据只请求一次encode声明字段映射比手动map更简洁后续新增图表只需加initXXXChart(globalDataset)无需新 fetch。6.3 用 Flask-WTF 添加基础 CSRF 保护满足企业级安全要求虽然结课作业不涉及敏感操作但添加 CSRF token 是良好习惯且一行代码即可启用# app.py from flask_wtf import FlaskForm from wtforms import StringField from wtforms.validators import DataRequired app.config[SECRET_KEY] your-secret-key-change-in-production # 必须设置 # 在 template 中 form 标签内加入 # input typehidden namecsrf_token value{{ csrf_token() }}安装依赖pip install Flask-WTF。此举让作业代码具备向生产环境平滑迁移的基础安全能力远超一般课程要求。验证 Flask 服务是否正常响应/api/sales/region_orders_q1时curl 命令应返回 JSON 数据而非 HTML 页面。本文还有配套的精品资源点击获取