【web應用】若依框架前端報表制作與導出全攻略(ECharts + html2canvas + jsPDF)

文章目錄

    • 前言
    • 一、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…