Python用Flask后端解析Excel图表,Vue3+ECharts前端动态还原(附全套代码)

Python用Flask后端解析Excel图表,Vue3+ECharts前端动态还原(附全套代码)

以下是完整的 Flask + Vue 3 前端模板 方案,实现 上传 Excel 文件(不再用链接),后端解析 chart1.xml,返回结构化数据,前端用 ECharts 渲染图表。

在这里插入图片描述

项目结构

project/ ├── app.py ├── templates/ │ └── index.html 

1. 后端:app.py

import xml.etree.ElementTree as ET import io from zipfile import ZipFile, BadZipFile from flask import Flask, render_template, request, jsonify from flask_cors import CORS # 可选,如果有跨域需求 app = Flask(__name__) CORS(app)# 允许跨域(同域其实不需要,但留着保险)defparse_chart_from_bytes(file_data): res ={} result ={}try: archive = ZipFile(io.BytesIO(file_data)) chart_data = archive.read('xl/charts/chart1.xml') res['code']=200 res['msg']="获取图表信息成功" tree = ET.parse(io.BytesIO(chart_data)) root = tree.getroot() ns ={'c':'http://schemas.openxmlformats.org/drawingml/2006/chart','a':'http://schemas.openxmlformats.org/drawingml/2006/main'}# 图表类型 type_mapping =[('pieChart','饼图','pie'),('lineChart','折线图','line'),('barChart','柱形图','bar'),('areaChart','面积图','line'),('scatterChart','散点图','scatter'),('radarChart','雷达图','radar'),] chart_type_cn ='其它' echarts_type ='line' is_area =Falsefor tag, cn, en in type_mapping:if root.find(f'.//c:{tag}', ns)isnotNone: chart_type_cn = cn echarts_type = en if cn =='面积图': is_area =Truebreak result['chart_type_cn']= chart_type_cn result['echarts_type']= echarts_type result['is_area']= is_area # 标题 title_text ="" title_elem = root.find('.//c:title/c:tx/c:rich', ns)if title_elem isnotNone:for t in title_elem.iterfind('.//a:t', ns):if t.text: title_text += t.text result['title']= title_text.strip()or"无标题"# 系列数据 series_list =[] ser_elements = root.findall('.//c:ser', ns)for idx, ser inenumerate(ser_elements):# 系列名称 name_elem = ser.find('c:tx/c:v', ns) name = name_elem.text if name_elem isnotNoneelsef"系列{idx +1}"# 类别(X轴) cat_vs = ser.findall('c:cat//c:pt/c:v', ns) categories =[v.text for v in cat_vs if v.text isnotNone]# 数值(Y轴) val_vs = ser.findall('c:val//c:pt/c:v', ns) values =[]for v in val_vs:if v.text:try: values.append(float(v.text))except ValueError: values.append(v.text) series_list.append({"name": name,"categories": categories,"data": values })# 共享类别轴(如果每个系列都没有独立类别)if series_list andall(len(s['categories'])==0for s in series_list): shared_vs = root.findall('.//c:cat//c:pt/c:v', ns) shared_cat =[v.text for v in shared_vs if v.text isnotNone]if shared_cat:for s in series_list: s['categories']= shared_cat # 饼图特殊处理if chart_type_cn =="饼图"and series_list:for s in series_list: pie_data =[] cats = s['categories']or[f"项{i+1}"for i inrange(len(s['data']))]for i, val inenumerate(s['data']): name = cats[i]if i <len(cats)elsef"项{i+1}" value = val ifisinstance(val,(int,float))else0 pie_data.append({"name": name,"value": value}) s['data']= pie_data # 最终类别轴(非饼图)if chart_type_cn !="饼图"and series_list and series_list[0]['categories']: result['categories']= series_list[0]['categories']else: result['categories']=[]# 系列(只保留 name 和 data) result['series']=[{"name": s["name"],"data": s["data"]}for s in series_list] res['data']= result except BadZipFile: res['code']=404 res['msg']="无效的Excel文件"except KeyError: res['code']=404 res['msg']="未找到图表信息(无chart1.xml)"except Exception as e: res['code']=500 res['msg']=f"解析失败: {str(e)}"return res @app.route('/')defindex():return render_template('index.html')@app.route('/api/upload_chart', methods=['POST'])defupload_chart():if'file'notin request.files:return jsonify({"code":400,"msg":"没有上传文件"})file= request.files['file']iffile.filename ==''ornotfile.filename.lower().endswith('.xlsx'):return jsonify({"code":400,"msg":"请上传 .xlsx 文件"}) file_data =file.read()return jsonify(parse_chart_from_bytes(file_data))if __name__ =='__main__': app.run(host='127.0.0.1', port=5000, debug=True)

2. 前端模板:templates/index.html

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>上传 Excel → ECharts 渲染</title><scriptsrc="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script><style>body{font-family: Arial, sans-serif;padding: 20px;background: #f5f5f5;}.container{max-width: 800px;margin: auto;background: white;padding: 30px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0,0,0,0.1);}input[type="file"]{padding: 10px;margin-bottom: 15px;}button{padding: 12px 24px;font-size: 16px;background: #409eff;color: white;border: none;border-radius: 4px;cursor: pointer;}button:hover{background: #66b1ff;}button:disabled{background: #a0cfff;cursor: not-allowed;}#chart{width: 100%;height: 600px;margin-top: 30px;}.msg{margin: 15px 0;font-weight: bold;color: #e6a23c;}</style></head><body><divid="app"class="container"><h2>上传 Excel 文件 → ECharts 渲染图表</h2><inputtype="file"accept=".xlsx"@change="onFileChange"/><button@click="uploadAndRender":disabled="!selectedFile">上传并渲染图表</button><divclass="msg">{{ message }}</div><divref="chartRef"id="chart"></div></div><script>const{ createApp, ref, onMounted, nextTick }= Vue;createApp({setup(){const selectedFile =ref(null);const message =ref('请选择一个包含图表的 .xlsx 文件');const chartRef =ref(null);let myChart =null;onMounted(()=>{ myChart = echarts.init(chartRef.value);});constonFileChange=(e)=>{const file = e.target.files[0];if(file && file.name.endsWith('.xlsx')){ selectedFile.value = file; message.value =`已选择文件:${file.name}`;}else{ selectedFile.value =null; message.value ='请上传 .xlsx 文件';}};constuploadAndRender=async()=>{if(!selectedFile.value)return; message.value ='正在上传并解析...';const formData =newFormData(); formData.append('file', selectedFile.value);try{const response =awaitfetch('/api/upload_chart',{method:'POST',body: formData });const json =await response.json();if(json.code !==200){ message.value =`错误:${json.msg}`;return;}const data = json.data;const option ={title:{text: data.title,left:'center',textStyle:{fontSize:18}},tooltip:{trigger: data.echarts_type ==='pie'?'item':'axis'},legend:{data: data.series.map(s=> s.name),top:30}};if(data.echarts_type ==='pie'){ option.series = data.series.map(s=>({type:'pie',name: s.name,radius:'55%',center:['50%','60%'],data: s.data,emphasis:{itemStyle:{shadowBlur:10,shadowOffsetX:0,shadowColor:'rgba(0, 0, 0, 0.5)'}}}));}elseif(data.echarts_type ==='radar'){const maxVal = Math.max(...data.series.flatMap(s=> s.data.filter(v=>typeof v ==='number')),100); option.radar ={indicator: data.categories.map(name=>({ name,max: maxVal *1.2}))}; option.series =[{type:'radar',data: data.series.map(s=>({name: s.name,value: s.data }))}];}else{ option.xAxis ={type:'category',data: data.categories }; option.yAxis ={type:'value'}; option.series = data.series.map(s=>({type: data.echarts_type,name: s.name,data: s.data,areaStyle: data.is_area ?{opacity:0.3}:undefined,smooth: data.echarts_type ==='line'?true:false}));}awaitnextTick(); myChart.setOption(option,true); message.value =`渲染成功!图表类型:${data.chart_type_cn}(${data.series.length} 个系列)`;}catch(err){ message.value ='上传或解析失败:'+ err.message; console.error(err);}};return{ selectedFile, message, chartRef, onFileChange, uploadAndRender };}}).mount('#app');</script></body></html>

使用方式

  1. 把上面的文件按结构放好。
  2. 浏览器打开 http://127.0.0.1:5000
  3. 选择一个包含图表(chart1)的 .xlsx 文件 → 点击“上传并渲染图表”

运行:

python app.py 

安装依赖:

pip install flask flask-cors 

功能特点

  • 支持上传本地 Excel 文件解析(安全,只读内存)。
  • 支持单/多系列图表。
  • 饼图、折线、柱形、面积、散点、雷达图基本还原。
  • 前端美化了些样式和交互。

相关文章

Python 使用 openpyxl 从 URL 读取 Excel 并获取 Sheet 及单元格样式信息
Python 解析 Excel 图表(Chart)信息实战:从 xlsx 中提取标题、字体和数据

Read more

基于Seedance WebGL WebRTC构建实时AI视频编辑全链路技术拆解

基于Seedance WebGL WebRTC构建实时AI视频编辑全链路技术拆解

【精选优质专栏推荐】《AI 技术前沿》—— 紧跟 AI 最新趋势与应用《网络安全新手快速入门(附漏洞挖掘案例)》 —— 零基础安全入门必看《BurpSuite 入门教程(附实战图文)》 —— 渗透测试必备工具详解《网安渗透工具使用教程(全)》 —— 一站式工具手册《CTF 新手入门实战教程》 —— 从题目讲解到实战技巧《前后端项目开发(新手必知必会)》 —— 实战驱动快速上手 每个专栏均配有案例与图文讲解,循序渐进,适合新手与进阶学习者,欢迎订阅。 文章目录 * 文章概要 * 引言 * 技术方案 * 流程介绍 * 核心内容解析 * 实践代码 * 常见误区与解决方案 * 总结 文章概要 本文探讨了视频创作与AI特效生成领域的关键技术,聚焦于Seedance视频生成模型、WebGL渲染、实时音视频处理、智能字幕生成以及多轨道编辑技术。这些技术共同构筑了现代视频制作的核心框架,帮助创作者从概念到成品实现高效转型。文章首先介绍视频创作的演进背景,然后阐述技术方案和整体流程。随后,通过核心内容解析深入剖析各模块的原理与应用,提供实践代码示例以供落地。本文旨在

2024前端文档预览避坑指南:为什么我放弃了微软Office Online接口?

2024前端文档预览避坑指南:为什么我放弃了微软Office Online接口? 去年我们团队接手了一个企业级知识库项目,其中文档预览模块的设计让我和同事们纠结了整整两周。最初,我们像大多数开发者一样,第一反应就是使用微软官方提供的Office Online接口——毕竟它看起来简单、免费,而且“官方”两个字自带光环。然而,随着项目深入和真实用户数据的涌入,我们很快发现这条路布满了暗坑。从文件大小限制导致的预览失败,到跨国访问时的龟速加载,再到样式渲染的种种不一致,每一个问题都在消耗用户的耐心和团队的开发时间。最终,我们痛下决心,彻底抛弃了这条看似捷径的道路,转向了自建文件转换服务结合PDF统一渲染的方案。这次转型不仅解决了当时的痛点,更为后续的系统扩展打下了坚实的基础。如果你也在为Word、Excel、PPT、PDF等文档的在线预览方案而头疼,尤其是面对中大型项目时对稳定性、性能和可控性的高要求,那么我踩过的这些坑,或许能帮你省下不少弯路。 1. 微软Office Online接口:看似完美的陷阱 刚开始接触文档预览需求时,几乎所有的技术博客和社区问答都会指向同一个方案:使用

ClaudeCode 深度运用:Figma-MCP 导出前端代码实现 UI 精准还原的方法

Figma-MCP 导出前端代码的核心原理 Figma-MCP(Multi-Component Platform)通过解析 Figma 设计文件的结构化数据,将图层、组件和样式转换为可维护的前端代码。其核心在于建立设计系统与代码库的映射关系,确保样式和布局的像素级还原。 设计稿预处理规范 设计稿需遵循严格的命名规范,图层和组件使用英文命名且避免特殊字符。颜色样式、文本样式和组件必须使用 Figma 的 Style 功能统一定义。响应式布局需明确标注约束条件,如固定宽度或自动拉伸。 MCP 配置文件编写 通过 mcp.config.json 定义代码生成规则: { "framework": "React", "cssPreprocessor": "scss", "exportPath": "./src/components", "

工业互联网CPS系统是一套前端基于Vue2.6,后端基于.NetCore3.1,前后端分离

工业互联网CPS系统是一套前端基于Vue2.6,后端基于.NetCore3.1,前后端分离

工业互联网CPS系统是一套前端基于Vue2.6,后端基于.NetCore3.1,前后端分离,支持跨平台、支持多租户、多语言、多数据库的平台型应用软件。 它涵盖了工业4.0领域主流的业务需求,如MES、WMS、SRM、EMS、QMS、Scada等。 本人深耕工业4.0领域多年,对传统实体企业数字化转型有着丰富的经验,本着自身扎实的技术,过硬的业务能力,开发了这套平台,希望可以帮助更多的企业早日实现工业转型改造。 引言 WMSCloud 是一套面向工业互联网场景的仓库管理系统(WMS),其核心为 CPS(Cyber-Physical Systems,信息物理系统)平台。该系统采用现代化的前后端分离架构:前端基于 Vue 2.6,后端基于 .NET Core 3.1,具备良好的跨平台能力与多租户支持。本文将从系统整体架构出发,深入剖析其关键模块设计与核心技术实现,帮助开发者和技术决策者快速掌握系统能力边界与扩展潜力。 一、