阿里开源的这个库,让 Excel 导出不再复杂(既要能写,还要写的好看)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howardliu.cn/effective-java-easyexcel-action-write/","title":"","type":null},"content":[{"type":"text","text":"前文","attrs":{}}]},{"type":"text","text":" 聊了 EasyExcel 的内容导出,本文主要说一下导出文件的格式化,格式化包括工作表/单元格样式和内容格式化。毕竟,有时候还是要看脸。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内容比较多,文内只会列出关键代码,想要完整源码,可以关注公号「看山的小屋」回复“easyexcel”获取。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"注解格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过注解定义格式是 EasyExcel 封装的高级功能,可以让我们很方便的定义格式。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"格式化内容","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先定义一个使用注解格式化内容的实体类:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Data\npublic class FormatContentItem {\n @ExcelProperty(value = \"字符串标题\", converter = TitleFormatConverter.class)\n private String string;\n @DateTimeFormat(\"yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒\")\n @ExcelProperty(value = \"日期标题\")\n private Date date;\n @NumberFormat(\"0.000%\")\n @ExcelProperty(\"数字标题\")\n private Double doubleData;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DateTimeFormat","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"NumberFormat","attrs":{}}],"attrs":{}},{"type":"text","text":"两个注解都是自带的注解,用于格式化时间和数字。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"DateTimeFormat","attrs":{}}],"attrs":{}},{"type":"text","text":"注解有两个属性,一个属性是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"value","attrs":{}}],"attrs":{}},{"type":"text","text":",用来定义时间格式,可以参考","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.text.SimpleDateFormat","attrs":{}}],"attrs":{}},{"type":"text","text":";另一个属性是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"use1904windowing","attrs":{}}],"attrs":{}},{"type":"text","text":",表示使用时间使用 1904 时间系统还是 1900 时间系统,默认是否。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"NumberFormat","attrs":{}}],"attrs":{}},{"type":"text","text":"注解有两个属性,一个属性是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"value","attrs":{}}],"attrs":{}},{"type":"text","text":",用来定义数字格式,可以参考","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.text.DecimalFormat","attrs":{}}],"attrs":{}},{"type":"text","text":";另一个属性是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"roundingMode","attrs":{}}],"attrs":{}},{"type":"text","text":",用来定义保留小数的方式,使用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.math.RoundingMode","attrs":{}}],"attrs":{}},{"type":"text","text":"枚举。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"想要格式化字符串,可以借助","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ExcelProperty","attrs":{}}],"attrs":{}},{"type":"text","text":"的 converter 属性,这个属性传入实现","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Converter","attrs":{}}],"attrs":{}},{"type":"text","text":"的类。比如示例中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"TitleFormatConverter","attrs":{}}],"attrs":{}},{"type":"text","text":",代码如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class TitleFormatConverter implements Converter {\n @Override\n public Class> supportJavaTypeKey() {\n return String.class;\n }\n\n @Override\n public CellDataTypeEnum supportExcelTypeKey() {\n return CellDataTypeEnum.STRING;\n }\n\n @Override\n public WriteCellData> convertToExcelData(String value, ExcelContentProperty contentProperty,\n GlobalConfiguration globalConfiguration) {\n return new WriteCellData<>(String.format(\"标题:%s(自定义)\", value));\n }\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d7/d7459fb97f8e41dd4528c950ead74d91.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"定义行高、列宽","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用注解定义行高的话,可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadRowHeight","attrs":{}}],"attrs":{}},{"type":"text","text":"定义表头高度,使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentRowHeight","attrs":{}}],"attrs":{}},{"type":"text","text":"定义表体高度,这个注解定义之后,所有表体高度都是相同的。列宽可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ColumnWidth","attrs":{}}],"attrs":{}},{"type":"text","text":"注解定义,这个注解可以定义在类上,表示整个表格的列都一样宽,也可以定义的属性上,表示指定列的宽度。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Data\n@HeadRowHeight(20)\n@ContentRowHeight(10)\n@ColumnWidth(25)\npublic class FormatCellItem {\n @ExcelProperty(\"字符串标题\")\n private String string;\n @ExcelProperty(\"日期标题\")\n private Date date;\n @ColumnWidth(50)\n @ExcelProperty(\"数字标题\")\n private Double doubleData;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c4/c4f64985e540d3bfc240a91e927dfa05.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"单元格定义样式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"控制单元格样式有四个注解:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadFontStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentFontStyle","attrs":{}}],"attrs":{}},{"type":"text","text":",这四个注解可以定义在类上作为全局表格的样式,也可以定义在字段上,作为当前列的样式。下面分别说一下这几个注解中比较常用的配置。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"*Style:分为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentStyle","attrs":{}}],"attrs":{}},{"type":"text","text":",分别定义表头和表体样式","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dataFormat:表头格式化,short 格式,是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"org.apache.poi.ss.usermodel.BuiltinFormats","attrs":{}}],"attrs":{}},{"type":"text","text":"类中已定义格式的小标","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"border*:分别是 borderLeft、borderRight、borderTop、borderBottom 四个属性,类型是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.enums.poi.BorderStyleEnum","attrs":{}}],"attrs":{}},{"type":"text","text":"枚举,用来定义表头单元格边框样式。边框的颜色也可以定义,使用、*BorderColor 定义即可。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fillPatternType:填充类型,类型是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.enums.poi.FillPatternTypeEnum","attrs":{}}],"attrs":{}},{"type":"text","text":"枚举,如果想要填充背景色,这个属性需要设置为","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SOLID_FOREGROUND","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fillForegroundColor:前景色,类型是 short,值却是使用的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"org.apache.poi.ss.usermodel.IndexedColors","attrs":{}}],"attrs":{}},{"type":"text","text":"枚举的 idx 值,只不过,两个类型不一致,一个是 short,一个是 int,没有办法直接引用。可见 java 中的依赖之间,还是有很多坑的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fillBackgroundColor:背景色,同","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"fillForegroundColor","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rotation:内容旋转角度","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"*FontStyle:有","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadFontStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentFontStyle","attrs":{}}],"attrs":{}},{"type":"text","text":",分别定义表头和表体的字体样式。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fontName:定义字体名称,类型字符串","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fontHeightInPoints:字号大小,类型是 short","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"italic:是否斜体,类型是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.enums.BooleanEnum","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"bold:是否加粗,类型是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.enums.BooleanEnum","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"strikeout:是否使用删除线(这个词本意是三振出局的意思,应该是与棒球有关)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"color:文本颜色,值使用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"org.apache.poi.ss.usermodel.IndexedColors","attrs":{}}],"attrs":{}},{"type":"text","text":",依然有类型不一致的情况","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"underline:下划线,类型是 byte,可以直接使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Font.U_NONE","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Font.U_SINGLE","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Font.U_DOUBLE","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Font.U_SINGLE_ACCOUNTING","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Font.U_DOUBLE_ACCOUNTING","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以这么定义:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Data\n// 头背景设置成红色 IndexedColors.RED.getIndex()\n@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)\n// 头字体设置成 20\n@HeadFontStyle(fontHeightInPoints = 20)\n// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()\n@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)\n// 内容字体设置成 20\n@ContentFontStyle(fontHeightInPoints = 20)\npublic class FormatStyleCellItem {\n // 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()\n @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)\n // 字符串的头字体设置成 20\n @HeadFontStyle(fontHeightInPoints = 30)\n // 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()\n @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)\n // 字符串的内容字体设置成 20\n @ContentFontStyle(fontHeightInPoints = 30)\n @ExcelProperty(\"字符串标题\")\n private String string;\n @ExcelProperty(\"日期标题\")\n private Date date;\n @ExcelProperty(\"数字标题\")\n private Double doubleData;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/38/384248552b672c8498126acd2308f57e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"类对象定义格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这种方式可以说是纯手工组装数据了,使用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.metadata.data.WriteCellData","attrs":{}}],"attrs":{}},{"type":"text","text":"类,这个类相当于是单元格的定义,通过设置","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.enums.CellDataTypeEnum","attrs":{}}],"attrs":{}},{"type":"text","text":"枚举类型的 type 属性,可以指明当前单元格格式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"守恒定律一直存在。这种方式灵活度很高,可以精细到具体的单元格格式,但是繁琐程度也增加了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"超链接","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"超链接使用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.metadata.data.HyperlinkData","attrs":{}}],"attrs":{}},{"type":"text","text":"类,需要设置地址、超链类型(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.metadata.data.HyperlinkData.HyperlinkType","attrs":{}}],"attrs":{}},{"type":"text","text":"枚举),然后将值写入到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WriteCellData","attrs":{}}],"attrs":{}},{"type":"text","text":"对象的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"hyperlinkData","attrs":{}}],"attrs":{}},{"type":"text","text":"属性即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 设置超链接\nHyperlinkData hyperlinkData = new HyperlinkData();\nhyperlinkData.setAddress(\"https://www.howardliu.cn\");\nhyperlinkData.setHyperlinkType(HyperlinkType.URL);\nWriteCellData hyperlink = new WriteCellData<>(\"网站\");\nhyperlink.setHyperlinkData(hyperlinkData);\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"备注","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"备注使用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.metadata.data.CommentData","attrs":{}}],"attrs":{}},{"type":"text","text":"类,需要设置作者、备注内容(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.metadata.data.RichTextStringData","attrs":{}}],"attrs":{}},{"type":"text","text":"类型),因为备注的默认大小是单元格大小,如果感觉太小,还可以设置相对高度和宽度。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 设置备注\nCommentData commentData = new CommentData();\ncommentData.setAuthor(\"Howard Liu\");\ncommentData.setRichTextStringData(new RichTextStringData(\"这是一个备注\"));\n// 备注的默认大小是按照单元格的大小 这里想调整到 4 个单元格那么大 所以向后 向下 各额外占用了一个单元格\ncommentData.setRelativeLastColumnIndex(1);\ncommentData.setRelativeLastRowIndex(1);\nWriteCellData comment = new WriteCellData<>(\"备注的单元格信息\");\ncomment.setCommentData(commentData);\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"公式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公式使用的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.metadata.data.FormulaData","attrs":{}}],"attrs":{}},{"type":"text","text":"类,可以直接设置","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"formulaValue","attrs":{}}],"attrs":{}},{"type":"text","text":"公式,不过官方不太推荐使用公式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 设置公式\nFormulaData formulaData = new FormulaData();\n// 将 123456789 中的第一个数字替换成 2\n// 这里只是例子 如果真的涉及到公式 能内存算好尽量内存算好 公式能不用尽量不用\nformulaData.setFormulaValue(\"REPLACE(123456789,1,1,2)\");\nWriteCellData formula = new WriteCellData<>();\nformula.setFormulaData(formulaData);\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"单元格格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过类定义单元格格式,与通过注解定义本质是一样的。所以与注解","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"HeadFontStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ContentFontStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"对应,设置单元格格式的类是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WriteCellStyle","attrs":{}}],"attrs":{}},{"type":"text","text":",设置字体的类是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WriteFont","attrs":{}}],"attrs":{}},{"type":"text","text":"。其中这些类的属性与注解的也是类似,不再赘述太多,直接上例子。(其实我觉得使用类定义格式的场景不多,真的碰到了,看看类定义就明白了)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 设置单个单元格的样式 当然样式 很多的话 也可以用注解等方式。\nWriteCellStyle writeCellStyleData = new WriteCellStyle();\n// 这里需要指定 FillPatternType 为 FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色。\nwriteCellStyleData.setFillPatternType(FillPatternType.SOLID_FOREGROUND);\n// 背景绿色\nwriteCellStyleData.setFillForegroundColor(IndexedColors.GREEN.getIndex());\nWriteCellData writeCellStyle = new WriteCellData<>(\"单元格样式\");\nwriteCellStyle.setWriteCellStyle(writeCellStyleData);\nwriteCellStyle.setType(CellDataTypeEnum.STRING);\n\n// 设置单个单元格多种样式\nRichTextStringData richTextStringData = new RichTextStringData();\nrichTextStringData.setTextString(\"红色绿色默认\");\n// 前 2 个字红色\nWriteFont writeFont = new WriteFont();\nwriteFont.setColor(IndexedColors.RED.getIndex());\nrichTextStringData.applyFont(0, 2, writeFont);\n// 接下来 2 个字绿色\nwriteFont = new WriteFont();\nwriteFont.setColor(IndexedColors.GREEN.getIndex());\nrichTextStringData.applyFont(2, 4, writeFont);\nWriteCellData richTest = new WriteCellData<>();\nrichTest.setType(CellDataTypeEnum.RICH_TEXT_STRING);\nrichTest.setRichTextStringDataValue(richTextStringData);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/20/205805c11774b72c29638d24cc5b4a8b.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"拦截器定义格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了直接使用类定义格式,我们还可以借助拦截器实现。(这里在名称上会有一些歧义,所用的类对象命名都是 xxxStrategy,翻译过来就是 xxx 策略,但是官方对其命名为拦截器)","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"已有拦截器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面示例中使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WriteCellStyle","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"WriteFont","attrs":{}}],"attrs":{}},{"type":"text","text":"可以实现单元格的样式,如果想要实现整行数据都是相同的格式,可以借助","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.write.style.HorizontalCellStyleStrategy","attrs":{}}],"attrs":{}},{"type":"text","text":"拦截器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * 使用已有策略实现自定义样式\n *\n *
    \n *
  • HorizontalCellStyleStrategy 每一行的样式都一样 或者隔行一样
  • \n *
  • AbstractVerticalCellStyleStrategy 每一列的样式都一样 需要自己回调每一页
  • \n *
\n */\nprivate static void writeByCellStyleStrategy() {\n String fileName = defaultFileName(\"writeByCellStyleStrategy\");\n\n // 表头策略\n WriteCellStyle headWriteCellStyle = new WriteCellStyle();\n // 背景设置为红色\n headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());\n WriteFont headWriteFont = new WriteFont();\n headWriteFont.setFontHeightInPoints((short) 40);\n headWriteCellStyle.setWriteFont(headWriteFont);\n\n // 表体策略\n WriteCellStyle contentWriteCellStyle = new WriteCellStyle();\n // 这里需要指定 FillPatternType 为 FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色。表头默认了 FillPatternType 所以可以不指定\n contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);\n // 背景绿色\n contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());\n WriteFont contentWriteFont = new WriteFont();\n // 字体大小\n contentWriteFont.setFontHeightInPoints((short) 20);\n contentWriteCellStyle.setWriteFont(contentWriteFont);\n\n // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现\n HorizontalCellStyleStrategy horizontalCellStyleStrategy =\n new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);\n\n // 这里 需要指定写用哪个 class 去写,然后写到第一个 sheet,名字为模板 然后文件流会自动关闭\n EasyExcelFactory.write(fileName)\n .head(Item.class)\n .registerWriteHandler(horizontalCellStyleStrategy)\n .sheet()\n .doWrite(sampleItems());\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/de/debdbc6ad0d91200425e564dd73b219d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如上面的结果,如果我们某个单元格数据比较长,可能会有遮挡,这个时候我们可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy","attrs":{}}],"attrs":{}},{"type":"text","text":"实现自动列宽调整。不过这个不太精确,但聊胜于无。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static void writeUseLongestMatchColumnWidthStyleStrategy() {\n String fileName = defaultFileName(\"writeUseLongestMatchColumnWidthStyleStrategy\");\n EasyExcelFactory.write(fileName)\n .head(Item.class)\n .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())\n .sheet()\n .doWrite(sampleItems());\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0b/0bd316ebf834435d3d2ef979f5bc9e45.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,确实不够精确。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"自定义拦截器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面展示的拦截器,都是实现了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.write.handler.WriteHandler","attrs":{}}],"attrs":{}},{"type":"text","text":"接口,然后使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"com.alibaba.excel.write.builder.AbstractExcelWriterParameterBuilder.registerWriteHandler","attrs":{}}],"attrs":{}},{"type":"text","text":"方法注册到写函数中。所以,我们也可能根据需要,自己定义需要的拦截器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这种自定义拦截器属于低级功能,需要了解很多底层设计和 API,鉴于篇幅,本文没有办法覆盖,这里只给出例子。如果有需要,可以留言沟通。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,我们需要某些单元格设置数据验证,展现形式就是下拉菜单,我们可以这样写:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ColumnValidationWriteHandler implements SheetWriteHandler {\n @Override\n public void afterSheetCreate(SheetWriteHandlerContext context) {\n // 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行\n CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0);\n DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();\n DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {\"测试 1\", \"测试 2\"});\n DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);\n context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们需要将某个单元格的格式设置为超链,也可以使用拦截器:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CellStyleWriteHandler implements CellWriteHandler {\n @Override\n public void afterCellDispose(CellWriteHandlerContext context) {\n Cell cell = context.getCell();\n // 这里可以对 cell 进行任何操作\n if (BooleanUtils.isTrue(context.getHead()) && cell.getColumnIndex() == 0) {\n CreationHelper createHelper = context.getWriteSheetHolder().getSheet().getWorkbook().getCreationHelper();\n Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);\n hyperlink.setAddress(\"https://www.howardliu.cn\");\n cell.setHyperlink(hyperlink);\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/82/82faa10d9a3d3e0a04befe45ddff1e19.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"合并单元格","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EasyExcel 提供的合并单元格功能比较简单,有两种方式:基于注解的合并、基于拦截器的合并。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"注解","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于注解的合并单元格提供了两个注解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"OnceAbsoluteMerge","attrs":{}}],"attrs":{}},{"type":"text","text":"注解实现指定位置的合并","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ContentLoopMerge","attrs":{}}],"attrs":{}},{"type":"text","text":"这个是内容的循环合并,指定某一列每几行合并。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 将第 6-7 行的 2-3 列合并成一个单元格\n@OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)\n@Data\npublic class MergeCellItem {\n @ContentLoopMerge(eachRow = 2)\n @ExcelProperty(\"字符串标题\")\n private String string;\n @ExcelProperty(\"日期标题\")\n private Date date;\n @ExcelProperty(\"数字标题\")\n private Double doubleData;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0f/0fb0bdb63c4bd40cc328d2a233f99c19.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"拦截器","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拦截器合并也是有两种,对应着注解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"OnceAbsoluteMergeStrategy","attrs":{}}],"attrs":{}},{"type":"text","text":",相对位置合并","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"LoopMergeStrategy","attrs":{}}],"attrs":{}},{"type":"text","text":"循环合并","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static void writeMergeCellCustom() {\n String fileName = defaultFileName(\"writeMergeCellCustom\");\n // 每隔 2 行会合并\n // 把 eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写\n LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);\n EasyExcelFactory.write(fileName)\n .head(Item.class)\n .registerWriteHandler(loopMergeStrategy)\n .sheet()\n .doWrite(sampleItems());\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结果为:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fb/fbe852e0dfc768800872416a12717482.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"文末总结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文从实战角度说了一下 EasyExcel 如果实现写出好看的表格,EasyExcel中提供了很多用于格式化的注解、拦截器,可以实现通用的格式化输出,如果还有更加个性化的格式要求,也可以自定义拦截器实现。接下来聊一下如何填充模板。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"推荐阅读","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howardliu.cn/effective-java-easyexcel-action-write/","title":"","type":null},"content":[{"type":"text","text":"阿里开源的这个库,让 Excel 导出不再复杂(简简单单的写)","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howardliu.cn/effective-java-easyexcel-action-write-pretty/","title":"","type":null},"content":[{"type":"text","text":"阿里开源的这个库,让 Excel 导出不再复杂(既要能写,还要写的好看)","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howardliu.cn/effective-java-easyexcel-action-fill/","title":"","type":null},"content":[{"type":"text","text":"阿里开源的这个库,让Excel导出不再复杂(填充模板的使用指南)","attrs":{}}]}]}]}],"attrs":{}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请点赞、收藏、关注。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#d4d4d4","name":"user"}}],"text":"👇🏻欢迎关注我的公众号「看山的小屋」,领取精选资料👇🏻","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9e/9e305acd1cca75053c144cb28adc6061.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章