Vue3前端导出表格数据-Excel

107 阅读3分钟

前端页面

image.png

导出Excel效果图

image.png

参数

  • 必传参数:headers表头(如图第二行),excelData表格数据
  • 可传参数:excelTitle标题(如图第一行), fileName文件名称, colWidth每列宽度, rowHeight每行高度, merges单元格合并(如图合并标题)

安装

pnpm install -xlsx
pnpm install xlsx-js-style

代码

<script setup lang="ts">
import { exportExcel } from "@/utils/excel";
//avue-crud的配置项
const option = {
	height: 400,
	selection: true,
	index: true,
	indexLabel: "序号",
	indexWidth: "100",
	showSummary: true,
	editBtn: false,
	addBtn: false,
	column: [
		{
			label: "姓名",
			prop: "userName",
			fixed: true
		},
		{
			label: "性别",
			prop: "sex"
		},
		{
			label: "电话",
			prop: "phone"
		},
		{
			label: "角色",
			prop: "role"
		},
		{
			label: "状态",
			prop: "status"
		}
	]
};
//表格数据
const data = [
	{ id: "1", userName: "张三", sex: "男", phone: "123455667", role: "管理员", status: "0" },
	{ id: "2", userName: "张三", sex: "男", phone: "123455667", role: "管理员", status: "0" },
	{ id: "3", userName: "张三", sex: "男", phone: "123455667", role: "管理员", status: "0" }
];
//表头
const headers = option.column.map(item => {
	return {
		key: item.prop,
		title: item.label
	};
});
//文件名
const fileName = ref();
//标题
const title = ref();

function exportTableData() {
	const excelOptions = {
		headers: headers,
		excelData: data,
		excelTitle: title.value,
		fileName: fileName.value
	};
	exportExcel(excelOptions);
}
</script>
<template>
	<div class="user">
		<h3>导出表格数据Excel</h3>
		<div style="display: flex">
			<span>文件名称</span><el-input v-model="fileName" style="width: 240px" placeholder="请输入导出的文件名称" /><br />
			<span>Excel表格标题</span><el-input v-model="title" style="width: 240px" placeholder="请输入Excel表格标题" />
		</div>
		<el-button @click="exportTableData">导出</el-button>
                <!--这里表格组件是avue的crud组件-->
		<avue-crud :data="data" :option="option"></avue-crud>
	</div>
</template>
<style lang="scss" scoped>
.user {
	padding: 20px;
	background-color: white;
	height: 94%;
}
</style>

//@/utils/excel.ts
import XLSX from "xlsx-js-style";
/**
 * 导出Excel的处理函数
 * title      excel标题	             示例:'用户表'
 * headers    excel表头 	             示例:[{key: 'userName', title: '姓名'},{key: 'sex', title: '性别'},{key: 'phone', title: '电话'}]
 * excelData  导出的数据 	             示例:[{id: '1', userName: '张三', sex: '男', phone: '123455667'}]
 * fileName   文件的名称	             示例:'导出表格数据'
 * colStyle   每列宽度,默认50磅	     示例:[{ wch: 10 }, { wch: 20 }, { wch: 20 }, { wch: 20 }, { wch: 30 }];
 * rowStyle   每行高度,默认30磅         示例:[{ hpx: 10 }, { hpx: 20 }, { hpx: 20 }, { hpx: 20 }, { hpx: 30 }];
 * merges     合并单元格,默认合并第一行  示例:s:开始位置, e:结束位置, r:行, c:列 [{ s: { r: 0, c: 0 }, e: { r: 0, c: 6 } },{ s: { r: 1, c: 1 }, e: { r: 1, c: 2 } },{ s: { r: 1, c: 3 }, e: { r: 1, c: 4 } },{ s: { r: 1, c: 5 }, e: { r: 1, c: 6 } },{ s: { r: 1, c: 0 }, e: { r: 2, c: 0 } }]
 * */
 //忽视我的ts类型,偷懒了
export interface excelOptions {
	/**
	 * Excel标题
	 */
	excelTitle?: any[];
	/**
	 * 导出文件名
	 */
	fileName?: string;
	/**
	 * Excel表头
	 */
	headers: any;
	/**
	 * 合并单元格
	 */
	merges?: any[];
	/**
	 * 导出表格数据
	 */
	excelData: any[];
	/**
	 * 单元格列宽
	 */
	colWidth?: any[];

	/**
	 * 单元格行高
	 */
	rowHeight?: any[];
}
export function exportExcel(options: excelOptions) {
	let { excelTitle, headers, excelData, fileName, colWidth, rowHeight, merges } = options;
	excelTitle = excelTitle ? [excelTitle] : ["Excel标题"];
	fileName = fileName || "导出表格数据";
	colWidth =
		colWidth ||
		headers.map(item => {
			return {
				wpx: 50
			};
		});
	rowHeight =
		rowHeight ||
		headers.map(item => {
			return {
				hpx: 30
			};
		});
	merges = merges || [
		// 标题单元格进行合并
		{ s: { r: 0, c: 0 }, e: { r: 0, c: (headers.length - 1) as number } }
	];

	// 单元格样式
	const cellStyle = {
		font: {
			bold: true,          //是否加粗
			sz: 12,              //字号
			name: "宋体"         //字体
		},
		alignment: {
			vertical: "center",  // 单元格内容垂直居中
			horizontal: "center" // 单元格内容水平居中
		},
		border: {                    //单元格边框
			top: { style: "thin", color: { rgb: "FF000000" } },
			bottom: { style: "thin", color: { rgb: "FF000000" } },
			left: { style: "thin", color: { rgb: "FF000000" } },
			right: { style: "thin", color: { rgb: "FF000000" } }
		}
	};
   //标题样式
	const headerStyle = {
		...cellStyle,
		fill: { 
			fgColor: { rgb: "87CEEB" } //单元格填充颜色
		}
	};

	const excel_title = excelTitle;
   //获取表头属性名
	const excel_headers_key = headers.map((item: any) => item.key);
   //获取表头属性值
	const excel_headers_title = headers.map((item: any) => item.title);
   //格式化表格数据生成二维数组
	const excel_data = excelData.map((item: []) => {
		let arr: any[] = [];
		excel_headers_key.forEach((key: any) => {
			arr.push(item[key]);
		});
		return arr;
	});
   //插入标题,表头(注意顺序)
	excel_data.unshift(excel_title,excel_headers_title);
   //新建一个工作簿
	let exc = XLSX.utils.book_new();
   //将二维数组转为工作表对象
	let exc_data = XLSX.utils.aoa_to_sheet(excel_data);
   //设置单元格宽度
	exc_data["!cols"] = colWidth;
  //设置单元格高度
	exc_data["!rows"] = rowHeight;
  //合并单元格
	exc_data["!merges"] = merges;
	// 应用样式到所有单元格
	excel_data.forEach((row: any, rowIndex: number) => {
		row.forEach((cell: any, colIndex: number) => {
			const cellRef = XLSX.utils.encode_cell({ c: colIndex, r: rowIndex }); // 获取单元格引用
			if (rowIndex === 0) {
				// 如果是标题行
				exc_data[cellRef].s = headerStyle; // 应用标题样式
			} else {
				// 其他行
				exc_data[cellRef].s = cellStyle; // 应用普通单元格样式
			}
		});
	});
  //将工作表添加到工作薄中
	XLSX.utils.book_append_sheet(exc, exc_data, fileName);
  //导出excel
	XLSX.writeFile(exc, fileName + ".xlsx", { bookType: "xlsx", type: "buffer" });
}