# 打印插件模型

## 插件模版

- 打印插件开发使用打印插件基类模版： `kd.bos.entity.plugin.AbstractPrintServicePlugin`  
- 插件接口为：`kd.bos.entity.plugin.IPrintServicePlugin`  

## 场景和示例

### beforeLoadData事件

场景：名称为【testdatasource】的数据源需要在插件中进行构造，不需要系统进行读取。

```java
	@Override
	public void beforeLoadData(BeforeLoadDataArgs e) {
		if(e.getDataSourceName().equalsIgnoreCase("testdatasource")) {
			//说明:此参数意味着名称为【testdatasource】的数据源需要插件提供数据包,系统不会自动取数据
			e.setCancel(true);
		}
	}
```

###  customPrintDataEntities事件

 场景：审批线路仅需要打印同意的记录。

```java
	@Override
	public void customPrintDataEntities(CustomPrintDataEntitiesArgs e) {	
		if ("workflow.approveline".equalsIgnoreCase(e.getDataSourceName())) {
			// 新的数据包
			List<DynamicObject> newDataEntities = new ArrayList<>();
			//遍历dataEntities，只保留同意的记录
			for(DynamicObject object : e.getDataEntities()) {
				Object resultName = object.get("resultName");
				if(resultName.toString().equals("同意")) {
					newDataEntities.add(object);
				}
			}			
			e.setDataEntities(newDataEntities);
		}
	}
```

场景：表单上单据头有一个文本控件，其内容是在表单插件根据一定条件动态赋值，同时打印时需要打印出来。

```java
  @Override
	public void customPrintDataEntities(CustomPrintDataEntitiesArgs e) {

		/* 方法一
		 *
		 * 克隆实体，复制数据包，然后对自定义字段赋值。
		 * 
		 *  */
		if (e.isMainDs()) {
			DynamicObjectType dt = null;
			//1、克隆实体对象
			try {
				dt = (DynamicObjectType) e.getDynamicObjectType().clone();
			} catch (CloneNotSupportedException e1) {
				log.error("克隆数据包失败：" + e);
			}

			if (dt != null && !e.getDataEntities().isEmpty()) {
				//2、注册字段
				//注意： 如果存在多个自定义字段的情况，可以通过遍历 e.getCustomFields()来注册
				String key = "customkey";
				dt.registerProperty(key, String.class, "custom", false);
								
				//3、复制生成新的数据包。 注意，复杂对象不会完整复制，只会复制id，请根据id自行加载。
				DynamicObject newObj = (DynamicObject) (new CloneUtils(false, false)).clone(dt, e.getDataEntities().get(0));
				
				//4、 为自定义字段赋值
				newObj.set(key, "我是自定义字段值");
				
				//5、 将新的数据包放入集合
				List<DynamicObject> dataEntities = new ArrayList<>();
				dataEntities.add(newObj);
				
				//6、将新的数据包集合设置回去取
				e.setDataEntities(dataEntities);
			}
		}
		
		
		/* 方法二
		 * 
		 * 自接对查询的数据包进行修改。
		 * 
		 *  */
		if (e.isMainDs()) {
			
			// 查询的数据包
			List<DynamicObject> dataEntities = e.getDataEntities();

			for (DynamicObject dataEntity : dataEntities) {
				// 注册字段
				String key = "customkey";
				dataEntity.getDynamicObjectType().registerProperty(key, String.class, "custom", false);
				// 为自定义字段赋值
				dataEntity.set(key, "我是自定义字段值");
			}
		}
	}
```

### beforeOuputElement事件

场景：当费用项目为差旅费时隐藏数据表格中列标识分别为 datacolumn1， datacolumn2， datacolumn3的三列。

```java
	@Override
	public void beforeOuputElement(OutputElementArgs e) {
		if (e.getOutput() instanceof PWDataGridColumn) {
			// 获取主数据源标识
			String dsName = e.getMainDataSourceName();
			// 字段标识
			String fieldKey = "expenseitem";
			// 获取主数据源字段的值
			Object expenseItem = e.getDataHelper().getValue(dsName, fieldKey);
			// 当费用项目为差旅费时隐藏列标识分别为 datacolumn1， datacolumn2， datacolumn3的三列
			// 差旅费设计为下拉列表，对应的值为 T，
			if (StringUtils.isNotBlank(expenseItem) && "T".equalsIgnoreCase(expenseItem.toString())) {
				if (e.getKey() != null) {
					switch (e.getKey()) {
					case "datacolumn":
					case "datacolumn2":
					case "datacolumn3":
						e.getOutput().setHide(true);
						break;
					default:
						// doSomethingElse();
						break;
					}
				}
			}
		}
	}
```

场景：设置网格某一单元格的背景色、字号、行间距、字间距等样式。
~~~java
	@Override
	public void beforeOuputElement(OutputElementArgs e) {
		//layoutcell4 为单元格标识
		if (e.getKey() != null && e.getKey().equalsIgnoreCase("layoutcell4")) {
			IPrintScriptable apw = e.getOutput();
			//字号
			apw.setProperty("style.font-size", 18); 
			//字颜色
			apw.setProperty("style.font-color", "#CD0000");
			//粗体
			apw.setProperty("style.font-bold", true);
			//斜体
			apw.setProperty("style.font-italic", true);
			//下划线
			apw.setProperty("style.underline", true);
			//删除线
			apw.setProperty("style.strikethrough", true);
			//背景色
			apw.setProperty("style.background", "#708090");	
			//水平对齐 left & center & right
			apw.setProperty("style.horizontal-align", "center");
			//垂直对齐 top & middle $ bottom
			apw.setProperty("style.vertical-align", "top");
			//折行
			apw.setProperty("style.text-wrap",true);
			//缩放
			apw.setProperty("style.text-shrink", true);
			//行间距
			apw.setProperty("style.line-spacing", 10);
			//字间距
			apw.setProperty("style.word-spacing", 5);
			//文本与边框间距离(目前处理为四边间距都相等)
			apw.setProperty("style.padding",10);
		}
	}
~~~



### afterOutputElement事件

场景： 标识为"code"的二维码控件，仅需要在第一页显示。

~~~java
	@Override
	public void afterOutputElement(OutputElementArgs e) {
		if (e.getKey() != null && e.getKey().equals("code")) {
			kd.bos.entity.plugin.IPrintScriptable apw = e.getOutput();
			int pageNumber = apw.getPageNumber();
			if (pageNumber > 2) {
				e.getOutput().setHide(true);
			}
		}
	}
~~~



### addDynamicColumns事件

场景： 数据表格需要动态生成两列，表头为三行，存在一个合并单元格(测试标题)，如下图：

![grid](.\image\grid.png)

```java
@Override
	public void addDynamicColumns(DynamicColumnArgs e) {
		//添加动态列
		if ("columnkey1".equals(e.getKey())) {
			List<DynamicColumn> dynamicColumns = new ArrayList<>();
			//bindfieldkey1为绑定的字段标识
			DynamicColumn col1 = new DynamicColumn("bindfieldkey1");
			LocaleString w1 = new LocaleString("15");
			 //设置宽度，单位为毫米mm，支持多语言
			col1.setWidth(w1);
			//是否统计
			col1.setStatistic(true);
			//统计范围 16:累计;17:区域小计;18:分组小计
			col1.setStatisticScope(16);
			//统计类型 1:求和;2:平均值;3:最大值;4:最小值;5:计数
			col1.setStatisticType(1);
			List<GridHeader> header1 = new ArrayList<>();
			header1.add(new GridHeader(1,"第1列"));
			header1.add(new GridHeader(2, "XL"));		
			col1.setGridHeader(header1);
			
			// bindfieldkey2为绑定字段标识
			DynamicColumn col2 = new DynamicColumn("bindfieldkey2");
			LocaleString w2 = new LocaleString("30");
			//设置宽度，单位为毫米mm，支持多语言
			col2.setWidth(w2);
			List<GridHeader> header2 = new ArrayList<>();
			header2.add(new GridHeader(1,"第2列"));
		    header2.add(new GridHeader(2, "XXL"));
			
			col2.setGridHeader(header2);

			dynamicColumns.add(col1);
			dynamicColumns.add(col2);
			e.setDynamicColumns(dynamicColumns);
			//设置合并的单元格
			List<MergeBlock> mBlocks = new ArrayList<>();
			MergeBlock mb = new MergeBlock(0, 0, 1, 2, "测试标题");
			mBlocks.add(mb);
			e.setMergeBlocks(mBlocks);
		}
	}
```



> 特别注意：addDynamicColumns事件需要由customPrintDataEntities事件提供数据。


## API概览

### AbstractPrintServicePlugin

| 方法名称              | 说明                                         |
| --------------------- | -------------------------------------------- |
| beforeLoadData         | 读取数据触发事件                                  |
| customPrintDataEntities      | 数据源处理事件                        |
| beforeOuputElement                 | 控件输出前事件                                 |
| afterOutputElement | 控件输出后事件                            |
|addDynamicColumns        | 动态列处理事件                          |


