(优于别人,并不高贵,真正的高贵应该是优于过去的自己。——海明威)
场景
根据订单参数生成账单PDF
结果
示例代码
/* eslint-disable no-unused-vars */
/* eslint-disable no-undef */
/* eslint-disable complexity */
const PDFDocument = require('pdfkit');
const fs = require('fs');
const dayjs = require('dayjs');
const AgencyWithdrawOrderStatus = {
Completed: 'Completed',
Submitted: 'Submitted',
Rejected: 'Rejected',
}
const agencyName = 'Jasin';
const agencyEmail = 'pdfkit@dev.com';
const order = {
"id": "FI-AWO-20230718-8657637839",
"accountID": {
"id": "ID-AGE-20230707-6351711990",
"type": "AGENCY"
},
"transactionNo": "FIAWO202307180650601146",
"referenceID": "",
"transferWay": "Bank card",
"walletID": "FI-WAL-20230707-5401898911",
"transferID": "FI-TRF-20230718-4134849682",
"transactionID": "FI-TRA-20230718-2048728248",
"somoServiceFee": 345,
"somoServiceFeeRate": 0.05,
"bankTransferFee": 3000,
"bankTransferFeeRate": 3000,
"paypalTransferFee": 345,
"paypalTransferFeeRate": 0.05,
"paypalRemittanceAmount": 6555,
"bankRemittanceAmount": 6900,
"totalAmount": 6900,
"status": "Submitted",
"transactionList": [{
"brandName": "asdasdads",
"transactionID": "FI-TRA-20230710-1494352833",
"brandID": "ID-BRA-20230627-2309656536",
"campaignID": "AC-CAM-20230710-3827733853",
"campaignTitle": "Jack",
"orderID": "AC-ORD-20230710-2795223591",
"type": "AGENCY_CAMPAIGN_ORDER_PAYMENT",
"amount": 6900,
"somoServiceFee": 345,
"paypalTransferFee": 345,
"bankTransferFee": 0
}, {
"brandName": "asdasdads",
"transactionID": "FI-TRA-20230710-5284753385",
"brandID": "ID-BRA-20230627-2309656536",
"campaignID": "AC-CAM-20230710-4072616238",
"campaignTitle": "Kami",
"orderID": "AC-ORD-20230710-7402268061",
"type": "AGENCY_CAMPAIGN_ORDER_PAYMENT",
"amount": 0,
"somoServiceFee": 0,
"paypalTransferFee": 0,
"bankTransferFee": 0
}],
"transactionCount": 2,
"accountBalance": 888750099,
"withdrawalMethod": {
"bankEnabled": true,
"bankAccountName": "12213",
"bankAccountNumber": "22",
"bankName": "333",
"bankAddress": "444",
"bankSwiftCode": "555"
},
"reason": "",
"createdAt": {
"$date": "2023-07-18T09:53:45.568Z"
},
"updatedAt": {
"$date": "2023-07-18T09:53:45.568Z"
},
"__v": 0
};
const method = async () => {
const pdfBuffer = await new Promise((resolve) => {
// 实例化PDF对象
const doc = new PDFDocument({ size: 'A4' });
const fontSize = 14;
const remittanceText = 'Remittance amount:';
const remittanceTitleColor = '#8792A2';
const remittanceAmountColor = '#181C1F';
const remittanceAmountSize = 40;
const orderStatusText = order.status;
const orderStatusColor = {
Submitted: '#4E515C',
Completed: '#009944',
Rejected: '#FF0000',
}[orderStatusText];
const orderStatusBackColor = {
Submitted: '#f2f4f9',
Completed: '#d8f7e6',
Rejected: '#f0dede',
}[orderStatusText];
const orderStatusOffset = {
Submitted: -6,
Completed: -8,
Rejected: 0,
}[orderStatusText];
const getFloatNumber = (number) => {
return parseFloat(number.toString()).toFixed(2);
};
const getConvertAmount = (number) => {
return number / 100;
};
const rejectedOffset = order.status === 'Rejected' ? 45 : 0;
const remittanceAmount = order.withdrawalMethod.paypalEnabled
? order.paypalRemittanceAmount
: order.bankRemittanceAmount;
const transferFee = order.withdrawalMethod.paypalEnabled
? order.paypalTransferFee
: order.bankTransferFee;
const remittanceAmountLength = getFloatNumber(
getConvertAmount(remittanceAmount),
).length;
const remittanceAmoutOffset =
remittanceAmountLength - 4 > 0 ? remittanceAmountLength - 4 : 0;
const transferAccount =
(order.withdrawalMethod.paypalEnabled
? order.withdrawalMethod.paypalAccountID
: order.withdrawalMethod.bankAccountName) ?? ' ';
const transferDetailTitleColor = '#F2F3F7';
const transferDetailTitleSize = 16;
const transferDetailBackgroundColor = '#171A1F';
const transferDetailTitleText = 'Transfer detail:';
const orderStatusX = 100;
const orderStatusY = 105;
const startX = 73;
const transferDetailRectY = 150;
const transferDetailX = 80;
const transferDetailY = 168;
const transferDetailWidth = 500;
const transferDetailHeight = 60;
const sideWidth = 500;
const sideHeight = 1;
const valueColor = '#272A33';
const linceColor = '#F5F7F7';
doc.
// 使用字体
font(`${__dirname}/Montserrat-Bold.ttf`).
// 字体大小
fontSize(fontSize).
// 字体颜色
fillColor(remittanceTitleColor).
// 这一行的文本
text(remittanceText).
fontSize(remittanceAmountSize).
fillColor(remittanceAmountColor).
text(`$ ${getFloatNumber(getConvertAmount(remittanceAmount))}`).
// 渲染块,指定x,y坐标以及宽高
rect(
orderStatusX + 112 + remittanceAmoutOffset * 23,
orderStatusY - 8,
92,
34,
).
// 块的颜色
fill(orderStatusBackColor).
// 块的字体大小
fontSize(fontSize).
// 字体的颜色
fillColor(orderStatusColor).
font(`${__dirname}/Montserrat-Regular.ttf`).
// 指定x,y坐标
text(
orderStatusText,
orderStatusX +
16 +
112 +
orderStatusOffset +
remittanceAmoutOffset * 23,
orderStatusY,
);
if (orderStatusText === AgencyWithdrawOrderStatus.Rejected) {
doc.
rect(startX, transferDetailRectY, 469, 30).
fill('#FFE58F').
// 插入图片
image(
'withdraw.pdf.rejected.png',
startX + 10,
transferDetailRectY + 5,
{ width: 20, height: 20 },
).
fontSize(12).
fillColor('#000000').
text(` ${order.reason}`, startX + 35, transferDetailRectY + 6.5);
}
doc.
rect(
startX,
transferDetailRectY + rejectedOffset,
transferDetailWidth,
transferDetailHeight,
).
fill(transferDetailTitleColor).
fontSize(transferDetailTitleSize).
fillColor(transferDetailBackgroundColor).
font(`${__dirname}/Montserrat-Bold.ttf`).
text(
transferDetailTitleText,
transferDetailX,
transferDetailY + rejectedOffset,
{
continued: false,
baseline: 'top',
},
).
font(`${__dirname}/Montserrat-Regular.ttf`).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text(
'Agency:',
startX,
transferDetailRectY + transferDetailHeight + 20 + rejectedOffset,
{ lineBreak: false },
).
fillColor(valueColor).
text(agencyName, startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text(`Email:`, startX, doc.y, { lineBreak: false, lineGap: 5 }).
fillColor(valueColor).
text(agencyEmail, startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Transaction No. :', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(order.transactionNo, startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Reference ID. :', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(order.referenceID || ' ', startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Transfer way:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(order.transferWay, startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Transfer account:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(transferAccount, startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Transfer status:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(orderStatusText, startX + 200, doc.y, { lineGap: 5 }).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Created time:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(
dayjs(order.createdAt.valueOf()).format('YYYY-MM-DD HH:mm:ss'),
startX + 200,
doc.y,
{ lineGap: 5 },
).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Update time:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(
dayjs(order.updatedAt.valueOf()).format('YYYY-MM-DD HH:mm:ss'),
startX + 200,
doc.y,
{ lineGap: 5 },
).
rect(startX, doc.y + 10, sideWidth, sideHeight).
fill(linceColor).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Order total amount:', startX, doc.y + 20, { lineBreak: false }).
fillColor(valueColor).
text(
`$ ${getFloatNumber(getConvertAmount(order.totalAmount))}`,
startX + 200,
doc.y,
{
lineGap: 5,
},
).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('SOMO service fee:', startX, doc.y, {
lineBreak: false,
}).
fillColor(remittanceTitleColor).
text(
`$ ${getFloatNumber(getConvertAmount(order.somoServiceFee))}`,
startX + 200,
doc.y,
{
lineGap: 5,
strike: true,
},
).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Transfer fee:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(
`$ ${getFloatNumber(getConvertAmount(transferFee))}`,
startX + 200,
doc.y,
{
lineGap: 5,
},
).
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Remittance amount:', startX, doc.y, { lineBreak: false }).
fillColor(valueColor).
text(
`$ ${getFloatNumber(getConvertAmount(remittanceAmount))}`,
startX + 200,
doc.y,
{
lineGap: 5,
},
).
rect(startX, doc.y + 10, sideWidth, 60).
fill('#F2F3F7').
fontSize(transferDetailTitleSize).
fillColor('#171A1F').
font(`${__dirname}/Montserrat-Bold.ttf`).
text('Associated with the order', transferDetailX, doc.y + 30, {
lineBreak: false,
baseline: 'top',
}).
text(' ', { lineGap: 10 }).
font(`${__dirname}/Montserrat-Regular.ttf`);
for (let i = 0; i < order.transactionList.length; i++) {
const item = order.transactionList[i];
const itemTransferFee = order.withdrawalMethod.paypalEnabled
? item.paypalTransferFee
: item.bankTransferFee;
doc.text(' ', { lineGap: 10 });
doc.
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Brand:', startX, doc.y, { paragraphGap: -20 }).
fillColor(valueColor).
text(item.brandName, startX + 200, doc.y, { lineGap: 8 });
doc.
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Campaign:', startX, doc.y, { paragraphGap: -20 }).
fillColor(valueColor).
text(item.campaignTitle, startX + 200, doc.y, { lineGap: 8 });
doc.
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Order ID:', startX, doc.y, { paragraphGap: -20 }).
fillColor(valueColor).
text(item.orderID, startX + 200, doc.y, { lineGap: 8 });
doc.
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Oder price:', startX, doc.y, { paragraphGap: -20 }).
fillColor(valueColor).
text(
`$ ${getFloatNumber(getConvertAmount(item.amount))}`,
startX + 200,
doc.y,
{
lineGap: 8,
},
);
doc.
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('SOMO service fee:', startX, doc.y, {
paragraphGap: -20,
}).
fillColor(remittanceTitleColor).
text(
`$ ${getFloatNumber(getConvertAmount(item.somoServiceFee))}`,
startX + 200,
doc.y,
{
lineGap: 8,
strike: true,
},
);
doc.
fontSize(fontSize).
fillColor(remittanceTitleColor).
text('Transfer fee:', startX, doc.y, { paragraphGap: -20 }).
fillColor(valueColor).
text(
`$ ${getFloatNumber(getConvertAmount(itemTransferFee))}`,
startX + 200,
doc.y,
{
lineGap: 8,
},
);
doc.rect(startX, doc.y + 20, sideWidth, sideHeight).fill(linceColor);
}
doc.end();
const buffers = [];
// 使用pdfkit的监听方法收集buffer,便于后续文件处理
doc.on('data', buffers.push.bind(buffers));
doc.on('end', () => {
const pdfData = Buffer.concat(buffers);
return resolve(pdfData);
});
});
fs.writeFileSync(`${__dirname}/file.pdf`, pdfBuffer);
};
method();