核心代码详见代码中的注释
<template> <div class="page"> <el-button type="primary" size="small" @click="exportOut">导出</el-button> <div class="tableBox"> <el-table :data="tableData" border :summary-method="getSummaries" show-summary > <el-table-column v-for="(item, index) in tableTitleList" :key="index" :prop="item.prop" :label="item.label" :width="item.width" align="center" > </el-table-column> </el-table> </div> </div> </template> <script> export default { data() { return { sumRow: {}, tableTitleList: [ { label: "序号", prop: "id", width: 100, }, { label: "姓名", prop: "name", }, { label: "射击次数", prop: "total", }, { label: "命中次数", prop: "hitNum", }, { label: "命中率", prop: "hitRate", }, ], tableData: [ { id: "1", name: "张三", total: 100, hitNum: 10, hitRate: "10%", }, { id: "2", name: "王五", total: 100, hitNum: 20, hitRate: "20%", }, ], }; }, methods: { // 导出为 csv 格式的文件 exportOut() { // 生成时间戳,避免每次导出的文件重名 let timeStamp = new Date().getTime(); // 在数据末尾添加合计行 let data = [...this.tableData, this.sumRow]; downloadCsv(this.tableTitleList, data, `导出的数据_${timeStamp}.csv`); }, // 自定义末尾的合计逻辑 getSummaries(param) { const { columns, data } = param; const sumDic = {}; columns.forEach((column, index) => { // 第 1 列 if (index === 0) { sumDic[column.property] = "合计"; return; } // 需特殊计算的列 if (column.property === "hitRate") { sumDic[column.property] = ((sumDic["hitNum"] / sumDic["total"]) * 100).toFixed(2) + "%"; return; } // 其他列默认求和 const values = data.map((item) => Number(item[column.property])); if (!values.every((value) => isNaN(value))) { // 可以求和的列 sumDic[column.property] = values.reduce((prev, curr) => { const value = Number(curr); if (!isNaN(value)) { return prev + curr; } else { return prev; } }, 0); } else { // 无法求和的列 sumDic[column.property] = "——"; } }); // 指定列添加单位 sumDic["total"] += " 次"; sumDic["hitNum"] += " 次"; // 获取末尾的合计行 this.sumRow = sumDic; return Object.values(sumDic); }, }, }; // 纯js导出 csv 格式文件 function downloadCsv(header, data, fileName = "导出结果.csv") { if ( !header || !data || !Array.isArray(header) || !Array.isArray(data) || !header.length || !data.length ) { return; } var csvContent = "data:text/csv;charset=utf-8,\ufeff"; const _header = header.map((h) => h.label).join(","); const keys = header.map((item) => item.prop); csvContent += _header + "\n"; data.forEach((item, index) => { let dataString = ""; for (let i = 0; i < keys.length; i++) { dataString += item[keys[i]] + ","; } csvContent += index < data.length ? dataString.replace(/,$/, "\n") : dataString.replace(/,$/, ""); }); const a = document.createElement("a"); a.href = encodeURI(csvContent); a.download = fileName; a.click(); window.URL.revokeObjectURL(csvContent); } </script> <style scoped> .page { padding: 30px; } .tableBox { margin: 20px 0px; } </style>