文章目录

    • 前言
    • 一、ECharts准备工作
      • 1. 检查ECharts安装
      • 2. 导入ECharts
      • 3. 创建饼图组件
      • 4. 模板部分
    • 二、报表导出功能实现
      • 1. 安装依赖
      • 2. 导入依赖
      • 3. 完整导出函数实现
      • 4. 样式优化
    • 三、完整组件实现
    • 四、常见问题与解决方案
      • 1. 图表截图不完整或模糊
      • 2. 图表背景透明
      • 3. 导出PDF中文乱码
      • 4. 跨域图片问题
    • 五、性能优化建议
    • 六、总结


前言

在若依Vue3框架中,实现报表功能是常见的业务需求。报表通常需要包含表格数据和可视化图表,并支持导出为PDF格式。本文将详细介绍如何使用ECharts实现数据可视化,结合html2canvas和jsPDF实现报表导出功能,并提供完整的代码实现和优化建议。

一、ECharts准备工作

1. 检查ECharts安装

首先需要确认项目中是否已安装ECharts。在项目根目录下执行以下命令:

npm list echarts

如果看到类似以下输出,则表示已安装:

└── echarts@5.4.3

如果没有安装,可以通过以下命令安装:

npm install echarts --save

2. 导入ECharts

在Vue组件中导入ECharts:

import * as echarts from 'echarts'
import { ref, onMounted, onBeforeUnmount } from 'vue'

3. 创建饼图组件

下面是一个完整的饼图实现示例,包含数据加载、图表渲染和销毁逻辑:

export default {setup() {const numbers = ref(['加载中', '加载中'])onMounted(() => {// 模拟数据加载setTimeout(() => {numbers.value = ['10', '30']const present = parseInt(numbers.value[0])const total = parseInt(numbers.value[1])const absent = total - present// 获取DOM元素const chartDom = document.getElementById('attendanceChart')if (!chartDom) return// 初始化图表const myChart = echarts.init(chartDom)// 图表配置const option = {tooltip: {trigger: 'item',formatter: '{a} <br/>{b}: {c} ({d}%)'},legend: {top: '0%',left: 'center',textStyle: {color: '#A6CAF4',fontSize: 14}},series: [{name: '出勤统计',type: 'pie',radius: '100%',top: '20%',data: [{value: present,name: '出勤',itemStyle: {color: '#91CC75'}},{value: absent,name: '缺勤',itemStyle: {color: '#409EF0'}}],label: {show: false},labelLine: {show: false},emphasis: {label: {show: true,position: 'inside',formatter: '{b}: {d}%',color: '#fff',fontSize: 14},itemStyle: {shadowBlur: 10,shadowOffsetX: 0,shadowColor: 'rgba(0, 0, 0, 0.5)'}}}]}// 应用配置myChart.setOption(option)// 响应式调整window.addEventListener('resize', function() {myChart.resize()})// 组件卸载时清理onBeforeUnmount(() => {window.removeEventListener('resize', () => {})if (myChart) {myChart.dispose()}})}, 300)})return { numbers }}
}

4. 模板部分

<template><div class="chart-container"><!-- 饼图容器 --><div id="attendanceChart" style="width: 100%; height: 300px;"></div><!-- 导出按钮 --><button @click="exportTextAndChartAsPDF" class="export-btn">导出报表</button></div>
</template>

二、报表导出功能实现

1. 安装依赖

确保已安装html2canvas和jsPDF:

npm install html2canvas jspdf --save

2. 导入依赖

import html2canvas from 'html2canvas'
import { jsPDF } from 'jspdf'

3. 完整导出函数实现

const personnelData = ref([{ name: '张三', date: '2023-10-01', status: '出勤' },{ name: '李四', date: '2023-10-01', status: '缺勤' },{ name: '王五', date: '2023-10-02', status: '迟到' },
])const exportTextAndChartAsPDF = async () => {// 创建PDF文档const pdf = new jsPDF('p', 'mm', 'a4') // 纵向A4const lineHeight = 10 // 行高let startY = 40 // 初始Y坐标// 1. 添加标题pdf.setFontSize(16).setTextColor(0, 0, 0)pdf.text('人员出勤报表', 105, 15, { align: 'center' })// 2. 添加表格标题行pdf.setFontSize(12)pdf.text('姓名', 20, 30)pdf.text('日期', 80, 30)pdf.text('状态', 140, 30)// 3. 添加数据行personnelData.value.forEach((item, index) => {const currentY = startY + index * lineHeightpdf.text(item.name, 20, currentY)pdf.text(item.date, 80, currentY)pdf.text(item.status, 140, currentY)})// 4. 截取饼图并添加到PDFconst chartContainer = document.getElementById('attendanceChart')?.parentNodeif (chartContainer) {try {// 截图饼图区域const canvas = await html2canvas(chartContainer, {scale: 2, // 提高分辨率logging: false,useCORS: true, // 允许跨域图片backgroundColor: '#FFFFFF', // 背景设为白色scrollY: -window.scrollY, // 解决滚动位置问题windowWidth: document.documentElement.scrollWidth, // 完整宽度windowHeight: document.documentElement.scrollHeight // 完整高度})// 计算饼图在PDF中的位置const imgProps = { width: 80, height: 80 } // 自定义饼图大小(mm)const imgX = 60 // X坐标(居中偏左)const imgY = startY + personnelData.value.length * lineHeight + 20 // Y坐标(表格下方留20mm间距)// 添加饼图到PDFpdf.addImage(canvas.toDataURL('image/png'),'PNG',imgX,imgY,imgProps.width,imgProps.height)} catch (error) {console.error('图表截图失败:', error)ElMessage.error('图表截图失败,请重试')return}}// 5. 保存PDFtry {pdf.save('人员出勤报表.pdf')ElMessage.success('报表导出成功')} catch (error) {console.error('PDF保存失败:', error)ElMessage.error('报表导出失败')}
}

4. 样式优化

<style scoped>
.chart-container {position: relative;width: 100%;height: 100%;padding: 20px;background-color: #fff;border-radius: 4px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}.export-btn {position: absolute;top: 10px;right: 10px;z-index: 10;padding: 8px 15px;background-color: #409EFF;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 14px;transition: all 0.3s;
}.export-btn:hover {background-color: #66b1ff;transform: translateY(-2px);box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}.export-btn:active {transform: translateY(0);
}
</style>

三、完整组件实现

<template><div class="chart-container"><!-- 饼图容器 --><div id="attendanceChart" style="width: 100%; height: 300px;"></div><!-- 导出按钮 --><button @click="exportTextAndChartAsPDF" class="export-btn">导出报表</button></div>
</template><script>
import * as echarts from 'echarts'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import html2canvas from 'html2canvas'
import { jsPDF } from 'jspdf'
import { ElMessage } from 'element-plus'export default {setup() {const numbers = ref(['加载中', '加载中'])const personnelData = ref([{ name: '张三', date: '2023-10-01', status: '出勤' },{ name: '李四', date: '2023-10-01', status: '缺勤' },{ name: '王五', date: '2023-10-02', status: '迟到' },])onMounted(() => {// 模拟数据加载setTimeout(() => {numbers.value = ['10', '30']const present = parseInt(numbers.value[0])const total = parseInt(numbers.value[1])const absent = total - presentconst chartDom = document.getElementById('attendanceChart')if (!chartDom) returnconst myChart = echarts.init(chartDom)const option = {tooltip: {trigger: 'item',formatter: '{a} <br/>{b}: {c} ({d}%)'},legend: {top: '0%',left: 'center',textStyle: {color: '#A6CAF4',fontSize: 14}},series: [{name: '出勤统计',type: 'pie',radius: '100%',top: '20%',data: [{value: present,name: '出勤',itemStyle: {color: '#91CC75'}},{value: absent,name: '缺勤',itemStyle: {color: '#409EF0'}}],label: {show: false},labelLine: {show: false},emphasis: {label: {show: true,position: 'inside',formatter: '{b}: {d}%',color: '#fff',fontSize: 14},itemStyle: {shadowBlur: 10,shadowOffsetX: 0,shadowColor: 'rgba(0, 0, 0, 0.5)'}}}]}myChart.setOption(option)window.addEventListener('resize', function() {myChart.resize()})onBeforeUnmount(() => {window.removeEventListener('resize', () => {})if (myChart) {myChart.dispose()}})}, 300)})const exportTextAndChartAsPDF = async () => {const pdf = new jsPDF('p', 'mm', 'a4')const lineHeight = 10let startY = 40pdf.setFontSize(16).setTextColor(0, 0, 0)pdf.text('人员出勤报表', 105, 15, { align: 'center' })pdf.setFontSize(12)pdf.text('姓名', 20, 30)pdf.text('日期', 80, 30)pdf.text('状态', 140, 30)personnelData.value.forEach((item, index) => {const currentY = startY + index * lineHeightpdf.text(item.name, 20, currentY)pdf.text(item.date, 80, currentY)pdf.text(item.status, 140, currentY)})const chartContainer = document.getElementById('attendanceChart')?.parentNodeif (chartContainer) {try {const canvas = await html2canvas(chartContainer, {scale: 2,logging: false,useCORS: true,backgroundColor: '#FFFFFF',scrollY: -window.scrollY,windowWidth: document.documentElement.scrollWidth,windowHeight: document.documentElement.scrollHeight})const imgProps = { width: 80, height: 80 }const imgX = 60const imgY = startY + personnelData.value.length * lineHeight + 20pdf.addImage(canvas.toDataURL('image/png'),'PNG',imgX,imgY,imgProps.width,imgProps.height)} catch (error) {console.error('图表截图失败:', error)ElMessage.error('图表截图失败,请重试')return}}try {pdf.save('人员出勤报表.pdf')ElMessage.success('报表导出成功')} catch (error) {console.error('PDF保存失败:', error)ElMessage.error('报表导出失败')}}return {numbers,exportTextAndChartAsPDF}}
}
</script><style scoped>
.chart-container {position: relative;width: 100%;height: 100%;padding: 20px;background-color: #fff;border-radius: 4px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}.export-btn {position: absolute;top: 10px;right: 10px;z-index: 10;padding: 8px 15px;background-color: #409EFF;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 14px;transition: all 0.3s;
}.export-btn:hover {background-color: #66b1ff;transform: translateY(-2px);box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}.export-btn:active {transform: translateY(0);
}
</style>

四、常见问题与解决方案

1. 图表截图不完整或模糊

  • 原因:html2canvas默认截图分辨率较低
  • 解决方案
    const canvas = await html2canvas(chartContainer, {scale: 2, // 提高截图分辨率windowWidth: document.documentElement.scrollWidth,windowHeight: document.documentElement.scrollHeight
    })
    

2. 图表背景透明

  • 原因:未设置背景色
  • 解决方案
    backgroundColor: '#FFFFFF' // 在html2canvas配置中设置
    

3. 导出PDF中文乱码

  • 原因:jsPDF默认不支持中文
  • 解决方案
    • 使用支持中文的字体(如ctex插件)
    • 或者将图表转为图片后插入PDF(本文采用的方法)

4. 跨域图片问题

  • 原因:图表中使用了跨域图片
  • 解决方案
    useCORS: true // 在html2canvas配置中启用
    

五、性能优化建议

  1. 懒加载图表:只在需要导出时才渲染图表
  2. 虚拟滚动:对于大数据量的表格,使用虚拟滚动技术
  3. 分页导出:数据量很大时,考虑分页导出
  4. Web Worker:将截图和PDF生成放在Web Worker中执行,避免阻塞UI

六、总结

本文详细介绍了在若依Vue3框架中实现报表功能的完整方案,包括:

  1. 使用ECharts实现数据可视化
  2. 使用html2canvas实现图表截图
  3. 使用jsPDF生成PDF文档
  4. 完整的错误处理和用户体验优化

通过本文的方案,你可以快速实现包含表格和图表的报表导出功能,并根据实际需求进行扩展和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88472.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88472.shtml
英文地址,请注明出处:http://en.pswp.cn/web/88472.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3+express联调接口时报“\“username\“ is required“问题

我用node .js的express框架写的登录接口&#xff0c;发现postman可以调通&#xff0c;但是vue3前端报错vue3我发现是我后端node.js的app.js入口文件中配置的解析前端参数的解析中间件和前端请求头中的Content-Type配置不一致的原因 解决方案 因为我后端配置解析表单数据的中间件…

《月亮与六便士》:天才的背叛与凡人救赎的残酷辩证法

当满地六便士成了庸人的火葬场​​毛姆笔下的斯特里克兰德&#xff0c;是一把捅穿中产幻梦的利刃。这个抛妻弃子、背叛友人的证券经纪人&#xff0c;在伦敦客厅的茶香与银勺碰撞声中&#xff0c;突然听见了远方的惊雷——“我必须画画”。如书中所言&#xff1a;​​“在满地都…

vue2往vue3升级需要注意的点(个人建议非必要别直接升级)

将 Vue 2 项目升级到 Vue 3 的过程中&#xff0c;需要重点关注以下几个难点和关键点&#xff1a; 建议小项目直接用vue3重写更快&#xff0c;bug更少 文章目录1. **Composition API 的学习与应用**2. **全局 API 的变更**3. **模板语法的兼容性变化**4. **组件选项和生命周期的…

聚焦数据资源建设与应用,浙江省质科院赴景联文科技调研交流

7月10日上午&#xff0c;浙江省质科院标准化中心副主任蒋建平、应珊婷等一行领导带队莅临景联文科技调研指导工作。双方围绕工业数据展开深度交流。座谈会上&#xff0c;景联文科技详细汇报了数据资源建设与应用方面的成果与规划&#xff0c;介绍了公共数据授权运营与对外合作的…

【Linux】系统引导修复

目录 开机引导过程 一.通电 二.BIOS环境检测 三.磁盘引导阶段 四.文件引导阶段 自动引导配置文件丢失修复 内核参数文件丢失修复 内核镜像文件丢失修复 内核初始化文件丢失修复 boot目录误删丢失修复 开机引导过程 磁盘引导阶段 /boot/grub2/grub.cfg #读取自动引…

2023年全国青少年信息素养大赛C++编程初中组决赛真题+答案解析

2023年全国青少年信息素养大赛C++编程初中组决赛真题+答案解析 编程题 第一题 判断是否存在重复的子序列 题目描述 从m 个字符中选取字符,生成n 个符号的序列,使得其中没有2 个相邻的子序列相同。 如从1,2,3,生成长度为5 的序列,序列“12321”是合格的,而“12323”和“12123”…

MySQL5.78.0锁表确认及解除锁表完全指南

目录 一、MySQL锁机制基础 1.1 锁的分类与作用 1.2 关键锁类型详解 二、锁表的常见原因与风险 2.1 引发锁表的典型场景 2.2 锁表的业务影响 三、锁表状态确认方法 3.1 基础工具&#xff1a;SHOW PROCESSLIST 3.2 MySQL 8.0锁信息查询&#xff08;推荐&#xff09; 3.2…

springboot生成pdf方案之dot/html/图片转pdf三种方式

文章目录pdf生成方案dot转pdfhtml转pdfopenhtmltopdfaspose-pdf实践playwright实践图片转pdfApache PDFBox实践框架场景匹配后记前言&#xff1a;随着客户对报告审美的提升&#xff0c;需求也越来越五彩斑斓~ 原有的dot模板已经满足不了他们了&#xff01;这篇文章主打列出各种…

前端开发—全栈开发

全栈开发者在面试前端或全栈岗位时&#xff0c;自我介绍需要巧妙融合“技术广度”与“岗位针对性”&#xff0c;避免成为泛泛而谈的“样样通样样松”。以下是结合面试官关注点和全栈特性的专业介绍策略&#xff1a;&#x1f9e0; 一、自我介绍的核心理念 突出全栈优势&#xff…

Redis生产环境过期策略配置指南:务实落地,避免踩坑

在生产环境中合理配置Redis过期策略是保障系统稳定性和内存效率的关键。以下配置建议基于实战经验&#xff0c;避免理论堆砌&#xff0c;直击核心要点&#xff1a;一、核心策略配置&#xff1a;惰性删除 定期删除&#xff08;默认已启用&#xff09;无需额外配置&#xff1a;R…

Ubuntu 20.04 安装 Node.js 20.x、npm、cnpm 和 pnpm 完整指南

&#x1f310; Ubuntu 20.04 安装 Node.js 20.x、npm、cnpm 和 pnpm 完整指南 &#x1f680; 在本文中&#xff0c;我们将介绍如何在 Ubuntu 20.04 上安装 Node.js 20.x&#xff0c;以及如何安装 npm、cnpm 和 pnpm 来提高开发效率 ⚡。1️⃣ 安装 Node.js 20.x 为了确保使用最…

【时时三省】(C语言基础)通过指针引用数组元素

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省引用一个数组元素&#xff0c;可以用下面两种方法&#xff1a;( 1 )下标法&#xff0c;如a[i]形式&#xff1b;( 2 )指针法&#xff0c;如* ( a i )或* ( p i )。其中a是数组名&#xff0c;p…

Guava LoadingCache

LoadingCache 是 Google Guava 库提供的一个高级缓存实现&#xff0c;它通过自动加载机制简化了缓存使用模式。核心特性自动加载机制当缓存未命中时&#xff0c;自动调用指定的 CacheLoader 加载数据线程安全&#xff1a;并发请求下&#xff0c;相同key只会加载一次灵活的过期策…

基于LSTM-GRU模型的黄金价格动态监测:关税政策与美指的量化关联研究

摘要&#xff1a;本文通过BERT-Large模型对关税政策进行语义解析&#xff0c;结合LSTM-GRU混合模型、DCC-GARCH动态相关性模型及蒙特卡洛情景分析&#xff0c;量化解析7月11日黄金价格异动背后的三大驱动因子——政策冲击、美元指数压制与美联储政策不确定性&#xff0c;提供AI…

V少JS基础班之第七弹

文章目录一、 前言二、本节涉及知识点三、重点内容1、prototype2、constructor3、中场回顾&总结4、__ proto__5、第二次中场回顾&总结6、原型链6、第三次中场回顾&总结7、原型链中的奇点一、 前言 第七弹内容是原型链。网络上原型链的资料很多。但是我看了很多篇&…

Nuxt3自动打包及自动修改端口号脚本

Nuxt3自动打包及自动修改端口号脚本技术文章大纲 背景与需求 Nuxt3作为现代Vue框架&#xff0c;开发中常需处理打包部署和端口配置问题。自动化脚本可提升效率&#xff0c;减少手动操作错误。 实现自动打包 利用Nuxt3内置命令结合Node.js脚本实现自动化构建。通过npm run build…

红海云国资案例之多层级工贸集团的一体化HR平台建设实战

在中国经济迈向高质量发展的进程中&#xff0c;国有企业作为重要的经济支柱和行业引领者&#xff0c;正面临着数字化转型的深刻变革。F集团作为G市首家实现工贸一体化运营的大型企业&#xff0c;位列中国轻工业百强&#xff0c;其在人力资源数字化转型中的探索和实践&#xff0…

TCP详解——流量控制、滑动窗口

目录 流量控制 滑动窗口 丢包重传 情况一&#xff1a;数据到达&#xff0c;应答丢失 情况二&#xff1a;数据包丢失 流量控制 TCP协议会根据接收端的缓冲区大小来调整发送速度&#xff0c;剩余空间多则发送速度快&#xff0c;否则降低发送速度 接收端将⾃⼰可以接收的缓…

C#高级特性面试问题的详细分析,涵盖核心概念、应用场景和最佳实践

序列化与反序列化 1. 什么是序列化和反序列化&#xff1f;用途是什么&#xff1f; // 序列化示例 Person person new Person { Name "Alice", Age 30 }; string json JsonSerializer.Serialize(person); // 序列化为JSON// 反序列化示例 Person deserialized Js…

【电脑】内存的基础知识

内存&#xff08;Memory&#xff09;是计算机中用于临时存储数据和程序的地方&#xff0c;它直接影响到系统的运行速度和性能。以下是关于内存的详细知识&#xff1a;1. 内存类型常见的内存类型包括以下几个主要种类&#xff1a;SDRAM (Synchronous Dynamic Random Access Memo…