# 表单插件模型

## 插件模版

- 表单插件开发使用普通单据的插件基类模版： `kd.bos.bill.plugin.AbstractBillPlugin`  

## 场景和示例

### 数据模型操作

场景：在表单全生命周期，对表单关联的模型进行赋值和取数操作；对单据体集合模型的处理；事件触发的操作对当前操作行模型处理。

```java
//获取单据模型
IDataModel model = this.getModel();
//获取当前操作行索引
int rowIndex = evt.getRowIndex();
DynamicObject voucher = model.getDataEntity(true);
//获取指定行字段值
long acctId = (long) model.getValue(Voucher.id_(Voucher.ACCT), rowIndex);
//设置字段值
model.setValue(Voucher._MAINCFAMT, 0);


//单据体集合模型取数
DynamicObjectCollection entries = this.getModel().getDataEntity()
							.getDynamicObjectCollection("entries");
for (DynamicObject entry : entries) {
    BigDecimal creditLoc = entry.getBigDecimal(Voucher.CR_LOC);
    //do sth
}


//单据体行上操作事件可使用getEntryCurrentRowIndex获取当前索引
IDataModel model = this.getModel();
//获取当前操作行索引
int index = model.getEntryCurrentRowIndex("entries");
model.setValue(id, "from", index);
//获取当前行指定字段
DynamicObject acct = (DynamicObject) model.getValue(Voucher.ACCT, curIndex);
```

### 页面初始化事件

场景：在页面打开时，设置页面默认状态、显示，注册组件监听器、设置过滤条件。

```java
//页面初始化
public void initialize() {
    super.initialize();
    TextEdit equityC = this.getControl(equityClose);
    equityC.addButtonClickListener(this);
    EntryGrid entryGrid = this.getControl(entryKey);
    entryGrid.addRowClickListener(this);
    addItemClickListeners("toolbarap");
}

//初始化时注册组件事件
public void registerListener(EventObject eventObject) {
    BasedataEdit account = this.getControl(ACCOUNT);
    //设置基础资料F7过滤条件
    //设置科目过滤条件
    account.addBeforeF7SelectListener(evt -> {
        FormShowParameter param = this.getView().getFormShowParameter();  
        QFilter orgF = BaseDataServiceHelper.getBaseDataFilter("bd_accountview", orgID);
        ListShowParameter paramd = (ListShowParameter) evt.getFormShowParameter();
        List<QFilter> filters = paramd.getListFilterParameter().getQFilters();
        filters.add(orgF);  
    });
}

```

### 点击事件示例

场景：绑定按钮、菜单、表格、超链接等的鼠标点击事件。

```java
/**
* 按钮点击事件触发，通过Key判断触发来源执行不同操作。
*/
public void click(EventObject evt) {
	super.click(evt);
	Control control = (Control) evt.getSource();
	String Key = control.getKey();
	if (Key.endsWith("btnOK")) {
		doOk(Key);
		return;
	}
}

/**
* 控件点击事件触发，通过Key判断触发来源执行不同操作。
*/
public void itemClick(ItemClickEvent evt) {
    String key = evt.getItemKey();
    // 保存操作
    if ("save".equals(key)) {
        //do save
    } else if ("add".equals(key)) {
        //do add
    } 
}


/*
*超链接点击
*/
public void hyperLinkClick(HyperLinkClickEvent evt) {
    String key = evt.getFieldName();
	if (Voucher.ACCT.equals(key)) {
        IDataModel model = this.getModel();
        //获取操作行索引
        int rowIndex = evt.getRowIndex();
        DynamicObject voucher = model.getDataEntity(true);
        //获取指定行单据体字段值
        long acctId = (long) model.getValue(Voucher.id_(Voucher.ACCT), rowIndex);
        long curId = (long) model.getValue(Voucher.id_(Voucher.CURRENCY), rowIndex);
        //do hyperLink operate
    }
}


/**
* 分录点击事件
*/
public void entryRowClick(RowClickEvent evt) {
    IDataModel model = this.getModel();
    Control ce = (Control) evt.getSource();
    int curIndex = evt.getRow();
    //获取分录标识
    String entryname = ce.getKey();
 	//do sth  
}

/**
* 分录双击事件
*/
public void entryRowDoubleClick(RowClickEvent evt) {
    IDataModel model = this.getModel();
    Control ce = (Control) evt.getSource();
    int curIndex = evt.getRow();
    //获取分录标识
    String entryname = ce.getKey();
 	//do sth  
}

/**
* 单元格点击事件
*/
public void cellClick(CellClickEvent evt) {
    String key = evt.getFieldKey();
    this.getView().setEnable(Voucher.EDESC.equals(key), "fillentry");
}

/**
* 单元格双击事件
*/
public void cellDoubleClick(CellClickEvent cellClickEvent) {

}
```

### before/after事件

场景：在页面生命周期和操作前后动态增加校验、改变页面、设置属性等。

```java
/**
*单据创建后触发，可完善单据数据，修改页面显示
*/
public void afterCreateNewData(EventObject e) {
    IDataModel model = this.getModel();
	model.setValue("type", "myCustomType");//设置模型值
    
    Label labelCotrol = this.getView().getControl("label1");
    labelCotrol.setText("标签示例名称");
}


//加载数据前操作
public void beforeBindData(EventObject e) {}
//加载数据后操作
public void afterBindData(EventObject e) {}


//执行操作
OperationResult result = OperationServiceHelper.executeOperate(operationKey, Voucher.K,
					ids.toArray(new Long[0]), oprOpt);
    
//操作前事件，通过setCancel可以取消当前操作
public void beforeDoOperation(BeforeDoOperationEventArgs args) {
    if (args.getSource() instanceof FormOperate) {
			FormOperate formOp = (FormOperate) args.getSource();
			String operateKey = formOp.getOperateKey();
			if (operateKey.equals("deleteentry")) {
                //do sth
                this.getView().showTipNotification(ResManager.loadKDString("当前操作验证失败", resourceID, "my-formplugin"));
                //取消当前操作
                args.setCancel(true);
            }
}

//操作后事件  operationResult.isSuccess() 获取操作状态  
public void afterDoOperation(AfterDoOperationEventArgs evt) {
		String name = evt.getOperateKey();
		OperationResult operationResult = evt.getOperationResult();
		if (name.equals("submit") && operationResult.isSuccess()) {
            this.getView().setVisible(false, "creator");
        }
}
    
    
public void beforeImportData(BeforeImportDataEventArgs e) {
		Map<String, Object> data = e.getSourceData();
		Map<String, Object> org = (Map<String, Object>) data.get(Voucher.ORG);
		String orgnumber = (String) org.get(Voucher.NUMBER);
    	data.put(Voucher.BOOKTYPE, bookType);
}

public void afterImportData(ImportDataEventArgs e) {
    
}

    
//删除分录前
public void beforeDeleteRow(BeforeDeleteRowEventArgs e) {   
    //获取删除操作行索引数组
    int[] index = e.getRowIndexs();
	IDataModel m = this.getModel();
			for (int i : index) {
                Object id = this.getModel().getEntryRowEntity(Voucher.E_K, i).getPkValue();
                //do sth
                this.getView().showTipNotification(ResManager.loadKDString("当前操作验证失败", resourceID, "my-formplugin"));
                //取消当前操作
                args.setCancel(true);
            }
}

//上传附件后，设置附件数量    
public void afterUpload(UploadEvent evt) {
	int length = evt.getUrls().length;
	int atts = (int) this.getModel().getValue("attachment");
	this.getModel().setValue("attachment", atts + length);
}

```

### 关闭回调

场景：设置在页面关闭时的回调方法/设置确认框关闭时的回调方法。

```java
@Override
public void closedCallBack(ClosedCallBackEvent evt) {
		super.closedCallBack(closedCallBackEvent);
		Object result = closedCallBackEvent.getReturnData();
    	//判断action
		switch (closedCallBackEvent.getActionId()) {
            case "VoucherBatchNum":
                //多选回调返回ListSelectedRowCollection
                ListSelectedRowCollection rows = (ListSelectedRowCollection) result;
                if (rows != null && rows.size() > 0) {
                    ListSelectedRow seldata = rows.get(0);
                    Object id = seldata.getPrimaryKeyValue();
                    //DO sth
                }
                break;
            case "autoExpEntity":
                //单选返回Map<String, Object>
                Map<String, Object> resultMap = (Map<String, Object>) e.getReturnData();
                if (resultMap.get("value") != null) {
                    String strObj = (String) resultMap.get("value");
                    String autoRowId = (String) resultMap.get("autoRowId");
                    //DO sth
                } 
                break;
		} 	
}



//确认操作注册回调事件
this.getView().showConfirm(
    ResManager.loadKDString("检索到相同摘要的模式凭证，是否引入？", "VoucherEdit_21", "fi-gl-formplugin"),
	MessageBoxOptions.YesNo, 
    new ConfirmCallBackListener("importFromTemplate")
);

public void confirmCallBack(MessageBoxClosedEvent messageBoxClosedEvent) {
    super.confirmCallBack(messageBoxClosedEvent);
    String callBackId = messageBoxClosedEvent.getCallBackId();
    if ("importFromTemplate".equals(callBackId)) {
        if (MessageBoxResult.Yes.equals(messageBoxClosedEvent.getResult())) {
            String templateId = this.getPageCache().get("templateId");
            addTemplate(Long.valueOf(templateId));
        }
    }
}
```

### 值改变事件

场景：在字段值发生改变时触发，获取新值校验、联动，获取旧值对比。

`注意在值改变时慎重使用setValue 避免出现死循环`

```java

public void beforePropertyChanged(PropertyChangedArgs e) {
    super.beforePropertyChanged(e);
    String key = e.getProperty().getName();   
    //值改变事件获取新值
    //兼容多选基础资料 此处返回数组
    //普通值改变事件取第一个
    Object newV = e.getChangeSet()[0].getNewValue();  
    Object oldV = e.getChangeSet()[0].getOldValue();
    if (key == null) {
        return;
    }
    //do sth
}

@Override
public void propertyChanged(PropertyChangedArgs e) {
    
}
```

## 事件监听器

继承表单插件模版的插件实现默认监听器IFormPlugin，提供事件编程入口

表单内置实现了几种监听器

- 模型数据创建事件  `kd.bos.entity.datamodel.IDataModelListener`
- 模型改变事件接口 `kd.bos.entity.datamodel.IDataModelChangeListener`
- 模型改变事件接口 `kd.bos.form.control.events.ItemClickListener`
- 模型改变事件接口 `kd.bos.form.control.events.ClickListener`
- 模型改变事件接口 `kd.bos.form.ICloseCallBack`



## API概览

| 方法名称              | 说明                                         |
| --------------------- | -------------------------------------------- |
| initialize            | 页面初始化                                   |
| registerListener      | 初始化时注册组件事件                         |
| click                 | 按钮点击事件                                 |
| itemClick             | 控件点击事件触发                             |
| hyperLinkClick        | 超链接事件                                   |
| entryRowClick         | 分录行格单击事件                             |
| entryRowDoubleClick   | 分录行格双击事件                             |
| cellClick             | 单元格点击事件                               |
| cellDoubleClick       | 单元格双击事件                               |
| afterCreateNewData    | 单据创建后触发，可完善单据数据，修改页面显示 |
| beforeBindData        | 加载数据前操作                               |
| afterBindData         | 加载数据后操作                               |
| beforeDoOperation     | 操作前事件，通过setCancel可以取消当前操作    |
| afterDoOperation      | 操作后事件                                   |
| beforeImportData      | 导入前事件                                   |
| afterImportData       | 导入后事件                                   |
| beforeDeleteRow       | 删除分录前                                   |
| afterUpload           | 上传附件后事件                               |
| closedCallBack        | 页面关闭时的回调方法                         |
| confirmCallBack       | 确认操作回调                                 |
| beforePropertyChanged | 值改变前处理事件                             |
| propertyChanged       | 值改变事件                                   |



## 界面打开参数

场景：请求加载表单维护界面时，需要设置的参数，设置界面上下文； 通过界面参数可以做父子页面参数传递、设置页面样式等操作。

详见 [界面打开参数](ShowParameter.md)

