PptxGenJS - 踩坑笔记
1. PptxGenJS关于Text 文字类型
1. 关于bullet
文字分段属性
正常情况下,每一条段落中间的一些文字没有其他样式要求时,一整条段落的文字样式都统一时,使用
bullet
属性是正常显示的,如:ppt生成下方文字大家好,我是TKEY。
我今年18岁。
我的工作是程序猿。
let page = pptx.addSlide() let text = [ { text: "大家好,我是TKEY", options: { bullet: true, }, }, { text: "我今年18岁。", options: { bullet: true, color: "4f81bd", }, }, { text: "我的工作是程序猿。", options: { bullet: true, color: "FF8C00", }, }, ] let textOptions = { x: 1, y: 1, w: 10, h: 2, isTextBox: true, paraSpaceAfter: 5, fontSize: 18, fontFace: "微软雅黑", } page.addText(text, textOptions)
但,当我们想赋予段落中几个文字予特殊的样式时,光这样写就不会在生效。如:当我想给上面TKEY以及程序猿这几个字额外的添加变红且加粗的样式时,就会出现下方的情况。
let text2 = [ { text: "大家好,我是", options: { bullet: true, }, }, { text: "TKEY", options: { bullet: true, bold: true, color: "ff0000", }, }, { text: "我今年", options: { bullet: true, color: "4f81bd", }, }, { text: "18岁。", options: { bullet: true, bold: true, color: "ff0000", }, }, { text: "我的工作是", options: { bullet: true, color: "FF8C00", }, }, { text: "程序猿。", options: { bullet: true, bold: true, color: "ff0000", }, }, ]
(可能大家觉得把TKEY、程序猿的bullet属性去掉就好,但去掉后这整个字段都不会有分段性质,大家阔以去试试)
正确的生效方法是:在TKEY、18岁的options对象中设置
breakLine:true
即手动换行,再使用一个align
文字对齐属性即可。let text2 = [ { text: "大家好,我是", options: { bullet: true, }, }, { text: "TKEY", options: { bullet: true, bold: true, color: "ff0000", breakLine: true, //是否换行 }, }, { text: "我今年", options: { bullet: true, color: "4f81bd", }, }, { text: "18岁。", options: { bullet: true, bold: true, color: "ff0000", breakLine: true, //是否换行 }, }, { text: "我的工作是", options: { bullet: true, color: "FF8C00", }, }, { text: "程序猿。", options: { bullet: true, bold: true, color: "ff0000", }, }, ] let textOptions2 = { x: 6, y: 1, w: 4, h: 3, isTextBox: true, paraSpaceAfter: 5, fontSize: 18, fontFace: "微软雅黑", align: "left", // 文字对齐方式 }
2. 关于PptxGenJS设置color颜色问题
因为PptxGenJS不能识别rgb颜色,只能识别hex16位进制且还不能加#号,因此我在网上找到一个方法,方便将rgb颜色转化为hex16进制。(主要也是因为我这个项目需求 需要将wangEditor编辑器输入的内容转化成PPT)
// 处理RGB颜色将RGB转化为hex16进制。 因为pptxgenjs 只能识别16进制的颜色值 export function rgbToHex(rgb) { // 默认颜色 if (!rgb || rgb.includes("rgb(0, 0") || rgb.includes("#")) { return "002060"; } const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); if (!match) { throw new Error(`Invalid RGB string: ${rgb}`); } const r = parseInt(match[1], 10); const g = parseInt(match[2], 10); const b = parseInt(match[3], 10); const hexR = r.toString(16).padStart(2, "0"); const hexG = g.toString(16).padStart(2, "0"); const hexB = b.toString(16).padStart(2, "0"); return `${hexR}${hexG}${hexB}`; }
2. PptxGenJS关于Table 表格类型
1. addTable的autopage自动分页功能 并没有那么好用
相信大家使用
addTable()
添加列表到PPT页面中时,或多或少都会遇见列表数据过多导致列表超出了当前PPT的空白页,如图片所示:当使用了
autoPage:true
之后,效果如下图所示:可以看到它直接将杜甫 诗词的字段截取了其中一部分到下一页,而不是以列为单位截取的,而且autoPage
生成的页面不可以进行额外的操作,例如增加任何的图片或者文字什么的(感觉鸡肋的)所以还是推荐大家手动的根据项目需求来进行对列表数据的截取分割,并且展示到PPT页面上。
2. addTable的单元格合并问题
PptxGenJS的单元格合并和我们传统的HTML的table表格一样,需要手动的赋予rowSpan colSpan属性,表示数据单元格跨越或延伸多少行,但需要注意将后续延伸后的单元格的值去掉,否则就会出现:
//数据: const tableData1 = [ [ { text: "姓名", options: { fill: "0070c0", color: "ffffff", bold: true }, }, { text: "年龄", options: { fill: "0070c0", color: "ffffff", bold: true }, }, { text: "职业", options: { fill: "0070c0", color: "ffffff", bold: true }, }, { text: "爱好", options: { fill: "0070c0", color: "ffffff", bold: true }, }, ], ["小明", { text: 18, options: { rowspan: 3 } }, "前端", "唱跳"], //合并三行 ["小红", "18", "后端", "rap"], ["小张", "18", "测试", "篮球"], ["小天", "20", "产品", "Music"], ]
由于rowspan衍生单元格,导致原本小红、小张的职业字段值被挤到了后面,小红、小张的年龄数据占到了职业这一列。
但后端一般都是一次性返回所有数据,以数组对象的形式,所以这就需要我们前端自己去筛选数据、清理数据、转换数据等操作。
因为我的项目所要求的功能是只需要按照一个合并ID字段来统一合并多少列的数据,我这里提供一下我的解决思路:
第一步:首先按照需要合并的ID,统计列表中关于此ID需要合并的数量,如:根据
mergeId
值来进行统计// 数据 let arr = [ { mergeId: "code1", id: "1", name: "小明", age: "18", job: "前端", favour: "唱跳", }, { mergeId: "code1", id: "2", name: "小红", age: "18", job: "后端", favour: "rap", }, { mergeId: "code1", id: "3", name: "小张", age: "18", job: "测试", favour: "篮球", }, { mergeId: "code2", id: "4", name: "小天", age: "20", job: "产品", favour: "Music", }, ] // 表格数据 合并 统计列表中包含合并ID 数量 // count: 统计mergeId相同的数据条数 // data: mergeId相同的数据 function pptTableDataMergeCell(arr, mergeId) { let codeGroups = arr.reduce((acc, cur) => { if (acc[cur[mergeId]]) { acc[cur[mergeId]].count++; acc[cur[mergeId]].data.push(cur); } else { acc[cur[mergeId]] = { count: 1, data: [cur] }; } return acc; }, {}); return codeGroups; }
打印输出
pptTableDataMergeCell
方法第二步:遍历方法的返回值,确定生成PPT表格所需要的字段名称(因为后端返回的数据还携带了其他的字段,需要确定以及清理),确定需要合并的列
mergeCol
,通过count
决定每个对象的第一条的rowspan
值,并且排除后面数据相同的被合并的字段值,确定排除的字段值mergeColName
let tableConfig = { mergeId: "mergeId", mergeColName: ["age"], mergeCol: [1], } let pptTableDataMap = ["name", "age", "job", "favour"] let mergeGroup = pptTableDataMergeCell(arr, 'mergeId') let tablePart = { text: [[...]], //包含表头的数据 options: { x: 0.5, y: 0.5, w: 12, fontSize: 16, fontFace: "微软雅黑", align: "center", valign: "middle", rowH: 0.5, colW: [3, 3, 3, 3], border: { pt: 1, color: "000000" }, }, } /** * @description 生成PptxGenJS 所需要的合并的 table表格数据 * @param {Array} dataMap 表格数据的字段 * @param {Object} mergeGroup 分组后的数据 * @param {Object} tableConfig 表格配置 * @param {Object} tablePart 表格包含表头数据 以及表格样式配置 * @returns {Array} 返回合并的表格数据 */ formateMergeTablePPT(dataMap, mergeGroup, tableConfig, tablePart) { // 生成的表格数据 第一维数组 let pptTableData = [] // 遍历 分类好的数据 Object.keys(mergeGroup).forEach((mergeKey) => { // 遍历 表格中的数据 mergeGroup[mergeKey].data.forEach((tableItem, index) => { let arr = [] // 第二数组 代表PPT table的行 // 根据 PPT需要的字段取出 表格中的数据 dataMap.forEach((tableKey, tableIndex) => { // 如果是合并的第一行,就添加合并的配置rowspan if (index == 0) { arr.push({ text: tableItem[tableKey], options: { // 判断当前是否是 需要合并的列 rowspan: tableConfig.mergeCol.includes(tableIndex) ? mergeGroup[mergeKey].count : 1, }, }) } else { // 保留没有被合并的数据 排除掉被合并的字段 if (!tableConfig.mergeColName.includes(tableKey)) { arr.push({ text: tableItem[tableKey], }) } } }) pptTableData.push(arr) }) }) tablePart.text.push(...pptTableData) return tablePart },
3. PptxGenJS关于Chart 图表类型
关于addChart
图表类型我项目上用的是最多的,且坑也是真滴多,下面列出一些比较经典的例子。
1. addChart的x、y定位属性不支持百分比的形式
PptxGenJS的图表类型关于x、y的定位属性只支持英寸形式定义,这好像是它们的一个BUG。原文地址在这:https://github.com/gitbrent/PptxGenJS/issues/468
解决:可以封装一个方法将百分比转化为英寸,如:
/** * 将百分比转换为英寸 * @param {Object} pptx PptxGenJS实例对象 * @param {Number} percentage 百分比值 * @param {Boolean} isY 是否是Y轴 */ function turnPercentageIntoInches(pptx, percentage, isY){ // 获取你的PPT画布尺寸 let layout = pptx.getLayout(); let size = (isY ? layout.height : layout.width)*1.1; return (size / 100000000)*percentage; }
2. chart的自定义图表的数据标签显示
PptxGenJS图表的自定义数据标签并不像echarts那么方便,addChart的数据标签包含x轴、y轴、数据显示等,只要携带
LabelFormatCode
后缀字段,其自定义都要参考微软的Number format codes微软的Number format codes是一种用于自定义数字格式的代码系统,用户可以按照自己的需求对数字进行格式化并显示。
这里介绍一下关于Number format codes的基本用法以及我在项目中常用到的一些示例:
"#":只显示数字。
"0":显示数字,如果位数不够则用0填充。
"0.00":显示数字,并保留两位小数。
"0.00\\%":保留两位小数,并且显示百分比单位
"#,##0":以千位分隔符格式显示数字。
"$#,##0.00":显示货币格式,并保留两位小数。
"0\;\;\;":保留补全整数,但数据为0时,不显示其数据标签,这个比较特殊,在一些特殊的图表中很有效果。
3. 单图表类型关于显示数据标签的弊端
addChart(chartType, chartData, chartOptions)
:使用单一图表类型生成图表时,当数据中有小数点需要显示数据标签时,需要再chartOptions中设置dataLabelFormatCode
属性,否则会默认将数据进行四舍五入显示出来。如数据标签只需要显示两位小数点时:
dataLabelFormatCode:'#.00'
,这样会使得当前图表的所有数据标签的显示都保留两位小数点。而且当你某条数据需要展示的小数点位数有很多时,你总不能在
dataLabelFormatCode
属性后一直加0吧。所以我建议,当图表中的数据包含小数点,且需要对应的显示出来时,可以使用组合式图表类型来进行生成图表。
组合式图表
addChart(chartTypes, chartOptions)
,当数据标签需要展示数据本身时,也需要用到dataLabelFormatCode
属性,不过不同的时,只需要设置dataLabelFormatCode
属性为空字符串即可。如:let chartTypes = [ { type: "line", data: [ { name: "班级平均分", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [80.6484, 75, 70, 84, 85, 90], }, ], options: { chartColors: ["FF8C00"], dataLabelFormatCode: "", //设置数据标签 }, }, { type: "bar", data: [ { name: "小明成绩", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [80.5, 75.44, 40, 50, 60, 70], }, { name: "小美成绩", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [100, 80, 70, 84, 85, 90], }, ], options: { dataLabelFormatCode: "", //设置数据标签 }, }, ]
4. 特殊柱状图:单一柱形图但颜色不一样
这个特殊图表主要还是自己在做的过程中遇到的一些特殊需求,分享出来供大家参考参考。
实现方法:利用组合式图表API,颜色不一的柱子主要是生成两个柱状图数据,并在数据上面设置null值以达到效果。
如:1月、2月是第一个柱状图数据,其
value
值只有两项:values: [80.5, 75.44]
,,而浅蓝色的数据占据3、4、5、6月而1月2月需要空出来,再设置
value
值时,就需要将1、2月的值设置为null
值,不显示出来:values: [null, null, 70, 84, 85, 90],
let chartData = [ { type: "bar", data: [ { name: "实际费用", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [80.5, 75.44], }, ], options: { chartColors: ["0070c0"], dataLabelFormatCode: "", }, }, { type: "bar", data: [ { name: "预测费用", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [null, null, 70, 84, 85, 90], }, ], options: { chartColors: ["9dc3e6"], dataLabelFormatCode: "", }, }, ]
5. 特殊折线图:单一折线图前半段实线,后半段虚线
实线方法:大体跟上述特殊柱状图实线相同,不过还是有两个需要注意的地方:
如:当你想要一月至二月之间为实线,其余的以虚线相连接,第二个line对象的
values
数组的第二个值不能为null
,也需要有对应的值,否则就会出现下面的场景另一个注意的地方是数据标签的显示问题,不知道大家有欸有注意上方的图的X轴部分有多余的0这一数据显示,当我们点击下方的0时,可以看到其实它这部分数据都是属于对应折线图的,像是PptxGenJS自动给你补全了你的数据。
这种时候,我们就可以用到
"0\;\;\;"
这一特殊的数据标签自定义了(当然,不需要展示数据标签的话当我没说🤡),直接上才艺!let chartData = [ { type: "line", data: [ { name: "实际费用", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [80.5, 75.44], }, ], options: { chartColors: ["c00000"], dataLabelFormatCode: "0.00\;\;\;", // 保留两位小数,且数据为0时,不显示数据标签 }, }, { type: "line", data: [ { name: "预测费用", labels: ["1月", "2月", "3月", "4月", "5月", "6月"], values: [null, 75.44, 70, 84, 85, 90], // 注意数据这块处理75.44 }, ], options: { chartColors: ["c00000"], lineDash: "dashDot", dataLabelFormatCode: "0.00\;\;\;", // 保留两位小数,且数据为0时,不显示数据标签 }, }, ]