フロントエンド開発プロセスでは、Excel をエクスポートする必要がよく発生します。この状況のほとんどは、サーバーがデータ (QAQ、少なくとも私が遭遇したほとんどのこと) を処理してからリンクを返すというもので、フロントエンドはサーバーの動作方法を気にする必要がなく、リンクを直接開きます。 (window.location.href = url ;) Excelをダウンロードできます。
しかし、物事はそれほど単純ではないことが多く、テーブルにデータがあまりなく、ページングがない場合、サーバーは Excel のエクスポート操作をフロントエンドにスローすることがよくあります。。。。フロントエンドで Excel をエクスポートする場面は多く、実行して渡すことが多いので、以下のように整理してレンダリングを共有しました。
最初はjs-xlsxプラグインを使用していましたが、基本的なテーブルのエクスポートにはまったく問題なく、非常に使いやすく、セルの結合もできます。ただし、新しい製品では、エクスポートされた Excel にスタイルを設定する必要があります。js-xlsx で試してみましたが、追加されたスタイルはエクスポート後に有効になりませんでした。オンラインでは、xlsx プラグインのオープンソース バージョンでは有効になると言われていました。 Excel スタイルの変更はサポートされていません。検索した結果、xlsx-styleが見つかりました。この 2 つの使用方法に大きな違いはなく、フロントエンドのエクスポートで完全に十分です。
それでは、早速コードに進みましょう。
最初のステップは、依存関係をインストールすることです。
// 如果使用xlsx-style(支持设置单元格样式)
npm i xlsx-style
// js-xlsx 不支持设置单元格样式
// npm i js-xlsx
注: npm i xlsx-style は、以下に示すように、インストール後に使用するとエラーを報告します。
解決策:
方法 1:
\node_modules\xlsx-style\dist\cpexcel.js を見つけて
、var cpt = require('./cpt' + 'able'); を var cpt = cptable; に変更します。方法 2 (推奨) :
chainWebpack:config => { config.externals({ './cptable': 'var cptable'}); }
2 番目のステップは、エクスポートされたメソッド ExcelUtils.js をカプセル化することです。
// import XLSX from 'xlsx';
import XLSX from 'xlsx-style';
/**
* 定制化导出excel(定制化:附加标题&&样式)
* @param { 表头 } headers
* @param { 数据源 } datasource
* @param { 表格副标题 } options
* @param { 配置文件类型 } type
* @param { 导出的文件名 } fileName
*/
function exportExcel(headers, datasource, options, type, fileName="未命名") {
// 处理列宽
const cloWidth = headers.map(item => (
{
wpx:item.width || 60}
))
// 处理附加表头
const _options = options.map((item,i) =>
Object.assign({
},{
title:item.title,
position:String.fromCharCode(65) + (i+1)
})
)
.reduce((prev, next) =>
Object.assign({
}, prev ,{
[next.position]:{
v:next.title }
}),{
},
)
// 处理表头
const _headers = headers.map((item,i) =>
Object.assign({
},{
key:item.dataIndex,
title:item.title,
position:String.fromCharCode(65+i) + (options.length+1)
})
)
.reduce((prev, next) =>
Object.assign({
}, prev ,{
[next.position]:{
v:next.title, key:next.key }
}),{
},
)
// 处理数据源
const _data = datasource.map((item,i) =>
headers.map((col,j) =>
Object.assign({
},{
content:item[col.dataIndex],position:String.fromCharCode(65+j) + (options.length + i + 2)
})
)
)
.reduce((prev, next) => prev.concat(next))
.reduce((prev, next) =>
Object.assign({
}, prev ,{
[next.position]:{
v:next.content}
}),{
}
)
const output = Object.assign({
}, _options, _headers, _data);
const outputPos = Object.keys(output); // 设置表格渲染区域,如从A1到C8
// 设置单元格样式!!!! 仅xlsx-style生效,js-xlsx写了也不生效
// 这里对每个单元格设置样式是写死的,每次改样式改都要改这里有点鸡肋
output.A1.s = {
font: {
sz: 14, bold: true, vertAlign: true },
alignment: {
vertical: 'center', horizontal: 'center' },
fill: {
bgColor: {
rgb: 'E8E8E8' }, fgColor: {
rgb: 'E8E8E8' } },
};
output.A2.s = {
font: {
sz: 12, bold: true, vertAlign: true },
alignment: {
vertical: 'center', horizontal: 'bottom' },
};
output.A3.s = {
font: {
sz: 12, bold: true, vertAlign: true },
alignment: {
vertical: 'center', horizontal: 'bottom' },
};
output.A4.s = {
font: {
sz: 12, bold: true, vertAlign: true },
alignment: {
vertical: 'center', horizontal: 'bottom' },
};
// 合并单元格
const merges = options.map((item,i)=> (
{
s: {
c: 0, r: i },
e: {
c: headers.length-1, r: i },
}
))
const wb = {
SheetNames: ['mySheet'], // 保存的表标题
Sheets: {
mySheet: Object.assign({
},
output, // 导出的内容
{
'!ref': `${
outputPos[0]}:${
outputPos[outputPos.length - 1]}`, // 设置填充区域(表格渲染区域)
'!cols': [...cloWidth],
'!merges': [...merges]
}
),
},
};
// 这种导出方法只适用于js-xlsx,且设置的单元格样式不生效,
// 直接打开下面这两行就行了,后面的可以省略
// XLSX.writeFile(wb,`${fileName}.xlsx`);
// return;
/**
* 以下这种导出方法对于js-xlsx/xlsx-style都适用
* 区别在于import XLSX from 'xlsx-style';可以设置单元格样式
* import XLSX from 'xlsx';不支持设置单元格样式
*
* new Blob转换成二进制类型的对象
*/
const tmpDown = new Blob(
[
s2ab(
XLSX.write(
wb,
{
bookType: type == undefined ? 'xlsx' : type.bookType, bookSST: false, type: 'binary' } // 这里的数据是用来定义导出的格式类型
)
),
],
{
type: '',
}
);
// 数据都准备完成,可以开始下载excel了
downExcel(tmpDown , `${
fileName + '.'}${
type.bookType == 'biff2' ? 'xls' : type.bookType}`);
}
/**
* <a>标签下载excel
* @param { Blob对象:二进制的数据 } obj
* @param { 文件名+文件类型后缀 } fileName
*/
function downExcel(obj, fileName) {
const a_node = document.createElement('a');
a_node.download = fileName;
// 兼容ie
if ('msSaveOrOpenBlob' in navigator) {
window.navigator.msSaveOrOpenBlob(obj, fileName);
} else {
// URL.createObjectURL根据传入的参数创建一个指向该参数对象的URL. 这个URL的生命仅存在于它被创建的这个文档里.
// 新的对象URL指向执行的File对象或者是Blob对象.
a_node.href = URL.createObjectURL(obj);
}
a_node.click();
// 每次调用createObjectURL的时候,一个新的URL对象就被创建了.即使你已经为同一个文件创建过一个URL.
// 如果你不再需要这个对象,要释放它,需要使用URL.revokeObjectURL()方法.
// 当页面被关闭,浏览器会自动释放它,但是为了最佳性能和内存使用,当确保不再用得到它的时候,就应该释放它.
setTimeout(() => {
URL.revokeObjectURL(obj);
}, 100);
}
// 字符串转字符流---转化为二进制的数据流
function s2ab(s) {
if (typeof ArrayBuffer !== 'undefined') {
const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf);
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
return buf;
} else {
const buf = new Array(s.length);
for (let i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff;
return buf;
}
}
export default exportExcel;
3 番目のステップは、ページ上の応答をエクスポートすることです。
const handleExcel = () => {
// 这里用的是模拟数据
const headers = [
{
title:"学员名字",dataIndex:'name',width:140},
{
title:"联系方式",dataIndex:'phone',width:140},
{
title:"状态",dataIndex:'status',width:140},
{
title:"扣除课时",dataIndex:'deduct',width:100},
{
title:"已完成/总课时",dataIndex:'number',width:100},
]
const datasource = [
{
name:"张三", phone:"12345678909", status:"已签到", deduct:1 ,number:"1/10" },
{
name:"李四", phone:"12345678909", status:"旷课",deduct:1 ,number:"1/10" },
{
name:"王小二", phone:"12345678909", status:"请假", deduct:'-' ,number:"0/10" },
{
name:"赵钱", phone:"12345678909", status:"已签到", deduct:1 ,number:"1/10" },
{
name:"孙李", phone:"12345678909", status:"已签到", deduct:1 ,number:"1/10" },
{
name:"马上飘", phone:"12345678909", status:"已签到", deduct:1 ,number:"1/10" },
]
const options = [
{
title: "高三数学寒假冲刺班" },
{
title: "班级:火箭1班" },
{
title: "上课时间: 2020-11-11 14:30~16:30" },
{
title: "上课老师:苏大强" },
]
const type = {
bookType: 'xlsx', bookSST: true, type: 'binary', cellStyles: true };
exportExcel(headers,datasource, options, type, "11月11日火箭1班签到表" );
}
補足:
データ量が多い場合は非推奨です。データ量が多くなるとエクスポートが遅くなり、1,000個のデータをエクスポートするのに数秒かかりますが、1,000個のデータをエクスポートするのに数分かかるため、プログラムがクラッシュしたのかと思いました。。。データの加工作業は狂ったループになるため、データ量が多すぎる場合は複数シートに書き出したり、Excelに書き出したりするなど、データを分けて加工することをお勧めします。
上記はすべての内容です。プロジェクトの実際の状況に応じて変更できます。不備があれば指摘することもできます~~