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

OpenPose Editor实战:AI绘画中的人物姿态精准控制技巧

OpenPose Editor实战:AI绘画中的人物姿态精准控制技巧 【免费下载链接】openpose-editoropenpose-editor - 一个用于编辑和管理Openpose生成的姿势的应用程序,支持多种图像处理功能。 项目地址: https://gitcode.com/gh_mirrors/op/openpose-editor 还在为AI绘画中的人物姿态不自然而烦恼吗?作为深度使用OpenPose Editor的创作者,我发现这款工具彻底改变了我的工作流程。今天分享一些实用的经验技巧,帮助你在AI绘画中实现精准的人物姿态控制。 从零开始的姿态编辑之旅 刚开始接触OpenPose Editor时,我也曾被那些复杂的骨骼点吓到。但经过多次实践,我总结出了一套简单有效的工作流程: 第一步:环境快速搭建 cd extensions git clone https://gitcode.com/gh_mirrors/op/openpose-editor 这个过程只需要几分钟,就能获得一个功能完整的姿态编辑工具。 第二步:界面熟悉与基础设置 OpenPose Edi

An efficient hardware architecture of integer motion estimation based on early termination and data

An efficient hardware architecture of integer motion estimation based on early termination and data

Zhang, Jun, Yu Zhang, and Hao Zhang. “An efficient hardware architecture of integer motion estimation based on early termination and data reuse for versatile video coding.” Expert Systems with Applications 242 (2024): 122706. 一、现存问题分析 1、由于降低搜索复杂度而降低搜索精度 目前已有的一些整数运动估计算法(如三步和四步搜索算法)通过简化搜索模板来降低运动估计的复杂度。然而,减少搜索点的数量和使用更小的搜索窗口会导致搜索算法陷入局部最优而不是全局最优,从而降低运动搜索的准确性。 2、由于增强搜索精度而导致高计算复杂度和资源消耗 另一种类型的整数运动估计算法(例如菱形搜索算法)采用复杂的搜索模板并增加搜索窗口内的搜索点的数量以提高搜索精度。复杂的运动搜索过程和额外的计算数据导致在视频编码期间显著的计算和存储资源消耗,这是以高成本来实现的。

【机器人】复现 StreamVLN 具身导航 | 流式VLN | 连续导航

【机器人】复现 StreamVLN 具身导航 | 流式VLN | 连续导航

StreamVLN 通过在线、多轮对话的方式,输入连续视频,输出动作序列。 通过结合语言指令、视觉观测和空间位姿信息,驱动模型生成导航动作(前进、左转、右转、停止)。 论文地址:StreamVLN: Streaming Vision-and-Language Navigation via SlowFast Context Modeling 代码地址:https://github.com/OpenRobotLab/StreamVLN 本文分享StreamVLN 复现和模型推理的过程~ 下面是示例效果: 1、创建Conda环境 首先创建一个Conda环境,名字为streamvln,python版本为3.9; 然后进入streamvln环境,执行下面命令: conda create -n streamvln python=3.9 conda activate streamvln 2、 安装habitat仿真环境

目标检测数据集——无人机视觉VisDrone数据集

目标检测数据集——无人机视觉VisDrone数据集

随着无人机技术的飞速发展,无人机在航拍、监控、农业、物流等领域的应用日益广泛。与此同时,无人机视角下的视觉任务,如目标检测、目标跟踪和场景理解,也成为了计算机视觉研究的热点。然而,相比传统的地面视角数据集,无人机视角下的图像具有高度变化、小目标密集、复杂背景等独特挑战,这对现有算法提出了更高的要求。 为了应对这些挑战并推动无人机视觉技术的发展,天津大学机器学习与数据挖掘实验室推出了 VisDrone数据集。作为一个大规模、标注精细的无人机视觉数据集,VisDrone 不仅涵盖了丰富的场景和多样化的目标类别,还为研究人员提供了一个极具挑战性的测试平台。无论是小目标检测的精度提升,还是密集场景下的鲁棒性优化,VisDrone 都成为了学术界和工业界不可或缺的资源。该数据集采集自中国14个不同城市,覆盖复杂城市场景、交通枢纽、密集人群等多种环境。 VisDrone官方Github下载渠道可点击访问: https://github.com/VisDrone/VisDrone-Dataset?tab=readme-ov-file 下载的数据集为VisDrone2019-DET-train