# CAP4自定義控件后端規范
## 1、自定義控件定義
在CAP4表單中,除了致遠提供的標準功能控件外,第三方開發可以通過自定義控件方案實現第三方的控件,這類控件我們統稱自定義控件。
## 2、自定義控件設計方案
### 2.1 總體設計方案
自定義控件由第三方開發,開發好之后上傳致遠商城,V5客戶的應用設計師登錄V5之后,在CAP4表單制作頁面左側會有自定義控件管理頁面,V5客戶可以在擴展控件列表頁面從商城下載第三方開發的控件到V5安裝環境中進行使用,模式如下圖:

*備注:V5用戶下載自定義控件只是將控件的包下載到了V5環境的共享目錄中,在下次重啟的時候才會將自定義控件包解壓進行自動部署。所以自定義控件下載之后需要重啟生效。*
#### 2.1.1 插件/component開發說明
第三方開發的自定義控件對于V5來說其實是一個插件或者component(*plugin,致遠內部開發自定義控件可以不用單獨創建插件,相關配置可以放到控件相關功能的插件配置中*),開發自定義控件將嚴格接受V5插件開發規范。 (*更多V5插件開發請見[http://open.seeyon.com/book/ctp/sdk/ctpbackendspec.html#插件化](http://open.seeyon.com/book/ctp/sdk/ctpbackendspec.html#%E6%8F%92%E4%BB%B6%E5%8C%96)*)
#### 2.1.2 自定義控件項目目錄結構說明
開發自定義控件第一步自然是創建項目,創建項目之后目錄結構可以參照以下示例創建各目錄,以下目錄結構是一個自定義控件的示例([下載完整目錄結構](code/custom_ctrl/seeyon.zip))。
~~~
─seeyon 固定目錄不可修改
├─apps_res 固定目錄不可修改
│ └─cap 固定目錄不可修改
│ └─customCtrlResources 固定目錄不可修改(存放PC端前端資源的目錄)
│ └─testCtrlResource
│ │ test.png
│ │
│ ├─html
│ │ param1Setter.html
│ │ param2Setter.html
│ │
│ └─js
├─m3files 固定目錄不可修改(存放M3移動端自定義資源包的目錄)
│ └─v5 固定目錄不可修改
│ 3423424234234234234.zip
│
└─WEB-INF 固定目錄不可修改
├─cfgHome 固定目錄不可修改
│ └─plugin 固定目錄不可修改
│ └─testCtrlPlugin
│ │ pluginCfg.xml
│ │
│ ├─i18n
│ │ export_to_js.xml
│ │ test_en.properties
│ │ test_zh_CN.properties
│ │ test_zh_TW.properties
│ │
│ └─spring
│ spring-test-manager.xml
│ spring-test-controller.xml
│
├─classes
│ └─com
│ └─seeyon
│ └─cap4
│ └─form
│ └─bean
│ └─fieldCtrl
│ TestCtrl.class
│
├─jsp 固定目錄不可修改
└─lib 固定目錄不可修改
~~~
#### 2.1.3 如何讓CAP4表單編輯器中出現你的自定義控件
* 按照以上示例中的目錄結構創建自定義控件項目工程
* 創建一個java類文件,實現自定義控件抽象類com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrl
* 實現或者重寫FormFieldCustomCtrl中的接口,示例(電子發票控件實現類):
`
~~~
private static final Log LOGGER = CtpLogFactory.getLog(FormEinvoiceCtrl.class);
/**
* 返回自定義控件唯一的key,控件key可以使用平臺www.seeyon.com.utils.UUIDUtil.getUUIDString()接口 生成一個uuid,將此Id作為控件的key
* @return
*/
@Override
public String getKey() {
return "4578843378267869145";
}
/**
* 獲取控件名稱
* @return
*/
@Override
public String getText() {
return ResourceUtil.getString("com.cap.ctrl.einvoice.text");
}
/**
* 定義此自定義控件是否支持批量刷新
*
* @return
*/
@Override
public boolean canBathUpdate() {
return false;
}
/**
* 此控件是否是附件類控件
*
* @return
*/
@Override
public boolean isAttachment() {
return true;
}
/**
* 新建的時候生成此自定義控件的值,此值會存放在對應動態表的對應字段上,一般情況下只有需要上傳附件類的自定義控件才需要此接口。
*
* @param oldVal
* @return
*/
@Override
public Object genVal(Object oldVal) {
if (StringUtil.checkNull(String.valueOf(oldVal))) {
return UUIDLong.longUUID();
} else {
return oldVal;
}
}
@Override
public List<String[]> getListShowDefaultVal(Integer integer) {
return null;
}
/**
* 初始值生成接口
*/
@Override
public String[] getDefaultVal(String s) {
return new String[0];
}
/**
* 控件初始化接口,此接口在控件初始化的時候,會調用,主要用于定義控件所屬插件id、在表單編輯器中的圖標、表單編輯器中有哪些屬性可以設置。
* 使用舉例:在接口中定義自定義控件在在表單編輯器中有哪些控件屬性需要配置
*/
@Override
public void init() {
this.setPluginId("formInvoiceBtn");//控件所屬插件id
this.setIcon("cap-icon-e-invoice");//控件在表單編輯器中的圖標
LOGGER.info("自定義控件" + this.getText() + "init執行開始");
ParamDefinition eivoiceDef = new ParamDefinition();//控件屬性設置定義對象
eivoiceDef.setDialogUrl("apps_res/cap/customCtrlResources/formEinvoiceCtrlResources/html/EinvoiceSetting.html");//控件屬性點擊之后彈出的設置對話框的url
eivoiceDef.setDisplay("com.cap.ctrl.einvoice.paramtext");//如果要做國際化 這個地方只能存key
eivoiceDef.setName("mapping");//控件屬性名
eivoiceDef.setParamType(Enums.ParamType.button);//控件屬性類型
addDefinition(eivoiceDef);
LOGGER.info("自定義控件" + this.getText() + "init執行結束,params.size:" + super.params.size());
}
/**
* 定義PC端自定義控件運行態資源注入信息
* path:文件夾路徑
* jsUri:定義PC端表單運行態加載第三方JavaScript的路徑
* cssUri:定義PC端表單運行態加載第三方CSS的路徑
* initMethod:定義PC端表單運行態第三方js入口方法名稱
* nameSpace:此自定義控件前端運行時的命名空間,可以參照一下寫法來定義命名空間
* @return
*/
@Override
public String getPCInjectionInfo() {
return "{path:'apps_res/cap/customCtrlResources/formEinvoiceCtrlResources/',jsUri:'js/formEinvoicePcRuning.js',initMethod:'init',nameSpace:'field_" + this.getKey() + "'}";
}
/**
* 獲取移動端自定義控件運行態資源注入信息
* path:'http://'+m3應用包mainifest.json中的urlSchemes的值+'v'+m3應用包mainifest.json中的version的值
* weixinpath: 微信端打開的時候使用的m3/apps/v5/自定義控件移動端資源目錄名稱/,weixinpath配置的就是此自定義控件移動端資源目錄名稱
* jsUri:移動端表單運行態加載第三方JavaScript的路徑
* initMethod:定義M3端表單運行態第三方js入口方法名稱
* * nameSpace:定義M3端表單運行態命名空間
*
* @return
*/
@Override
public String getMBInjectionInfo() {
return "{path:'http://einvoice.v5.cmp/v1.0.0/',weixinpath:'invoice',jsUri:'js/formEinvoiceMbRuning.js',initMethod:'init',nameSpace:'field_"+this.getKey()+"'}";
}
/**
* 定義控件對應數據庫所需長度
* 注意:一旦自定義控件長度定好,上線部署到OA環境中之后,
* 此接口返回的長度不允許改變,因為上線之后如果有表單使用
* 了此自定義控件,就會在數據庫中創建字段長度為此指定長度
* 的數據列。
*
* @return
*/
@Override
public String getFieldLength() {
return "25";
}
/**
* 是否支持套紅,自定義控件默認false,如果支持需要重新接口返回true
*
* @return
*/
@Override
public boolean canInjectionWord() {
return false;
}
~~~
#### 2.1.4 如何讓CAP4報表查詢配置中出現你的自定義控件
- 實現或者重寫FormFieldCustomCtrl中的接口中的canShowInReport;
- 實現或者重寫FormFieldCustomCtrl中的接口中的convertCtrlValue;
- 實現或者重寫FormFieldCustomCtrl中的接口中的getRenderInfo4Run
```
@Override
public FormFieldCustomCtrlReportInfo canShowInReport() {
FormFieldCustomCtrlReportInfo reportInfo = new FormFieldCustomCtrlReportInfo();
reportInfo.setEnableQuery(true);
reportInfo.setEnableDisplayField(true);
reportInfo.setEnableSort(true);
reportInfo.setEnableFilterField(true);
return reportInfo;
}
@Override
public Object convertCtrlValue(FormFieldBean fieldBean, Object value) {
return MapUtils.getString(JSONUtil.parseJSONString((String) value, Map.class), "projectName");
}
@Override
public FormFieldCustomCtrlRunInfo getRenderInfo4Run() {
FormFieldCustomCtrlRunInfo runInfo = new FormFieldCustomCtrlRunInfo();
// PC列表
FormFieldCustomCtrlInjectInfo pcList = new FormFieldCustomCtrlInjectInfo();
pcList.setPath("apps_res/cap/customCtrlResources/projectRelatedResources/");
pcList.setNameSpace(getListNameSpace());
pcList.setJsUri("js/projectRelatedPCList.umd.min.js");
runInfo.setPcList(pcList);
// PC普通篩選
FormFieldCustomCtrlInjectInfo pcFilter = new FormFieldCustomCtrlInjectInfo();
pcFilter.setPath("apps_res/cap/customCtrlResources/projectRelatedResources/");
pcFilter.setNameSpace(getFilterNameSpace());
pcFilter.setJsUri("js/projectRelatedPCFilter.umd.min.js");
runInfo.setPcFilter(pcFilter);
// 移動端列表
FormFieldCustomCtrlInjectInfo mList = new FormFieldCustomCtrlInjectInfo();
mList.setPath("http://customCtrlResources.v5.cmp/v1.0.0/");
mList.setWeixinPath("customCtrlResources");
mList.setNameSpace(getListNameSpace());
mList.setJsUri("projectRelatedResources/js/projectRelatedMList.umd.min.js");
runInfo.setMobileList(mList);
// 移動端普通篩選
FormFieldCustomCtrlInjectInfo mFilter = new FormFieldCustomCtrlInjectInfo();
mFilter.setPath("http://customCtrlResources.v5.cmp/v1.0.0/");
mFilter.setWeixinPath("customCtrlResources");
mFilter.setNameSpace(getFilterNameSpace());
mFilter.setJsUri("projectRelatedResources/js/projectRelatedMFilter.umd.min.js");
runInfo.setMobileFilter(mFilter);
return runInfo;
}
/**
* 獲取列表區作用域
*
* @return
*/
private String getListNameSpace() {
return getNameSpace("List");
}
/**
* 獲取條件區作用域
*
* @return
*/
private String getFilterNameSpace() {
return getNameSpace("Filter");
}
/**
* 獲取全局命名空間
*
* @param uniqueKey
* @return
*/
protected String getNameSpace(String uniqueKey) {
return StringUtils.uncapitalize(getClass().getSimpleName()) + uniqueKey + getKey();
}
```
#### 2.1.5 在spring配置文件中配置自定義控件Java類
控件實現類創建好之后需要在spring配置文件中將此類配置一下,由spring管理此類,具體做法是在你的項目中創建如2.1.2中的spring目錄和目錄下的文件spring-xxx-manager.xml(xxx由第三方自己定義),編輯此xml文件,按照spring規范定義此bean對象即可,例如電子發票控件的spring配置:
~~~
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" default-autowire="byName">
<bean id="formInvoiceBtnCtrl" class="com.seeyon.cap4.form.bean.fieldCtrl.FormEinvoiceCtrl"/>
</beans>
~~~
### 2.2 自定義控件屬性設置開發
### 2.2.1 定義自定義控件有哪些屬性
自定義控件在表單編輯器中,需要由開發控件的人自己定義控件有哪些屬性,屬性由com.seeyon.cap4.form.bean.ParamDefinition進行描述,需要在自定義控件實現類的init方法中進行屬性的定義和添加,屬性設置分兩種表現形式,此處著重描述按鈕類型的屬性怎么定義: 在自定義控件的init方法中new出一個ParamDefinition類型的對象,調用對象的setDialogUrl方法設置按鈕點擊之后彈出框的url(如果類型是按鈕類的才需要),調用對象的setDisplay方法設置按鈕顯示的名稱,調用setParamType設置屬性類型(類型由枚舉com.seeyon.cap4.form.util.Enums.ParamType進行描述,button:按鈕類型,text文本框類型),最后調用addDefinition將new出來的對象添加到自定義控件屬性列表中,示例代碼:
~~~
ParamDefinition eivoiceDef = new ParamDefinition();
eivoiceDef.setDialogUrl("apps_res/cap/customCtrlResources/formEinvoiceCtrlResources/html/EinvoiceSetting.html");
eivoiceDef.setDisplay("com.cap.ctrl.einvoice.paramtext");//如果要做國際化 這個地方只能存key
eivoiceDef.setName("mapping");
eivoiceDef.setParamType(Enums.ParamType.button);
addDefinition(eivoiceDef);
~~~
### 2.2.2 如何開發自定義控件屬性設置頁面
如果是彈出框類型的屬性,需要開發彈出框所需的頁面,在頁面中定義一個名稱為ok的js方法,此方法返回點擊確定之后所需要保存的設置信息,在表單編輯器保存表單定義的時候,CAP會將自定義控件屬性的定義信息保存到cap\_form\_definition表的field\_info對應字段的customParam屬性中,field\_info是json格式。
~~~
function OK(){
var mapping = [];
var settings = $("#systemMappingArea").find(".biz_groupguanlian");
var checkTag = true;
for(var i=0;i<settings.length;i++){
var setting = $(settings[i]);
var temp = {};
var einvoicefield = setting.find(".leftField").find("option:selected").attr("fieldtype");
var fieldname = setting.find(".rightField").find("option:selected").attr("fieldname");
if(einvoicefield===undefined||fieldname===undefined){
top.$.alert("選項不能為空!");
checkTag = false;
break;
}
temp.source = einvoicefield;
temp.target = fieldname;
mapping.push(temp);
}
if(checkTag){
var hasSame = false;
var sameFieldName = "";
//校驗所選是否滿足規則
//先校驗右側同一個數據域是否出現多次
for(var j=0;j<mapping.length;j++){
var current1 = mapping[j];
for(var k=0;k<mapping.length;k++){
var current2 = mapping[k];
if(current1.source!=current2.source && current1.target===current2.target){
hasSame = true;
sameFieldName = current1.target;
break;
}
}
if(hasSame){
break;
}
}
//再校驗左右是否類型匹配
if(hasSame){
checkTag = false;
var sameField = getFieldInfoByCurrentField(initObj.currentfield.name,sameFieldName);
top.$.alert(sameField.display+"[右側數據域]出現多次!");
}else{
var errorMsg = "";
var formId = initObj.formBaseInfo.formBaseInfo.formInfo.id;
$.ajax({ url: "/seeyon/rest/cap4/formEinvoice/checkMapping",
async:false,
type: 'POST',
dataType:'json',
contentType : 'application/json;charset=UTF-8',
data: JSON.stringify({"formId":formId,"datas":mapping}),
success: function(data){
if(data.data.result=="true"){
checkTag = true;
}else{
checkTag = false;
errorMsg = data.data.errorMsg;
}
}
});
if(!checkTag){
top.$.alert(errorMsg);
}
}
}
return {valid:checkTag,data:mapping};
}
~~~
表單保存之后數據庫cap\_form\_definition中field\_info字段截圖:
* 顯示為一個按鈕,點擊之后是彈出框形式,如圖:
* 顯示為一個文本框
### 2.2.2 自定義控件前端運行態渲染開發
CAP4表單自定義控件前端渲染分為PC端和移動端(M3或者微協同統稱移動端),CAP4平臺會將您渲染自定義控件的js文件動態注入到頁面中。您需要通過2.1.3中所描述的getPCInjectionInfo和getMBInjectionInfo接口來分別定義您自定義控件在PC和移動端渲染的js路徑。表單在運行態調用的時候,會主動調用您js文件中的初始化方法,您需要在初始化方法中實現您自定義控件的渲染以及事件綁定等操作。如下為表單電子發票控件的PC端js文件代碼:
~~~
(function (ctx,factory) {
var nameSpace = 'field_4578843378267869145';
if(!window[nameSpace]){
var Builder = factory(ctx,nameSpace);
window[nameSpace] = {
instance: {}
};
window[nameSpace].init = function (options) {
window[nameSpace].instance[options.privateId] = new Builder(options);
};
}
})(typeof $ === 'undefined' ? top.$ : $,function($,domain){
function App(options) {
var self = this;
self.appendChildDom = function (adaptation, messageObj, privateId, getData) {
self.customButton(adaptation, messageObj, privateId, getData);
};
self.customButton = function (adaptation, messageObj, privateId, getData) {
if (adaptation) {
self.adaptation = adaptation;
}
//----------創建DOM---------
var domStructure = '';
var box = document.querySelector('#' + privateId);
if(!box) {
console.warn('未找到控件dom');
return;
}
if (getData.auth === 'browse') {
domStructure = '<section class="cap4-images is-one "><div><div class="cap4-images__cnt" style="border:none;"><div class="cap4-images__items"><div class="cap4-images__holder"></div></div></div></div></section>';
}else if (getData.auth === 'hide') {
box.style.display="none";
return;
}else {
domStructure = '<section class="cap4-images is-one "><div><div class="cap4-images__cnt"><div class="cap4-images__items"><div class="cap4-images__holder"></div></div><div class="cap4-images__picker upload ' + privateId + '"><div class="icon CAP cap-icon-e-invoice"></div></div></div></div></section>';
};
box.innerHTML = domStructure;
if (self.getData.attachmentInfo.attachmentInfos && self.getData.attachmentInfo.attachmentInfos.length > 0) {
var filename=encodeURIComponent(self.getData.attachmentInfo.attachmentInfos[0].filename);
if (getData.auth === 'browse') {
box.querySelector(".cap4-images__items").innerHTML = '<div style="cap4-images__it"><a style="display: block;max-height: 20px;" target="myFormIframe" href="/seeyon/fileDownload.do?method=download&fileId=' + self.getData.attachmentInfo.attachmentInfos[0].fileUrl + '&v=' + self.getData.attachmentInfo.attachmentInfos[0].v + '&createDate=' + self.getData.attachmentInfo.attachmentInfos[0].createdate + '&filename=' +filename + '"title="' + filename + '">'+self.getData.attachmentInfo.attachmentInfos[0].filename + '</a></div>';
} else {
box.querySelector(".cap4-images__items").innerHTML = '<div style="cap4-images__it"><a style="display: block;max-height: 20px;" target="myFormIframe" href="/seeyon/fileDownload.do?method=download&fileId=' + self.getData.attachmentInfo.attachmentInfos[0].fileUrl + '&v=' + self.getData.attachmentInfo.attachmentInfos[0].v + '&createDate=' + self.getData.attachmentInfo.attachmentInfos[0].createdate + '&filename=' +filename + '"title="' + filename + '">' +self.getData.attachmentInfo.attachmentInfos[0].filename + '</a><div class="cap4-images__close"><i class="icon CAP cap-icon-guanbi delete"></i></div></div>';
}
box.querySelector('.upload') && (box.querySelector('.upload').style.display = "none");
if (box.querySelector('.delete')) {
box.querySelector('.delete').addEventListener('click', function () {
var field = box.querySelector(".cap4-images__items");
field.removeChild(field.childNodes[0]);
box.querySelector('.upload') && (box.querySelector('.upload').style.display = "");
self.aId = self.getData.attachmentInfo.attachmentInfos[0].id;
if (self.adaptation.backfillFormAttachment) {
var attachmentData = [
{
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
updateRecordId: self.getData.recordId || '',
handlerMode: 'delete',
fieldName: self.getData.id,
deleteAttchmentData: [self.aId]
}
];
self.adaptation.backfillFormAttachment(JSON.parse(JSON.stringify(attachmentData)), privateId);
}
});
}
}else{
if (getData.auth === 'edit'){
box.querySelector(".cap4-images__cnt").style.background=(messageObj.isNotNull=='1'?'#fef0d0':'');
}
}
if (document.querySelector('.' + privateId)) {
document.querySelector('.' + privateId).addEventListener('click', function () {
if(self.messageObj.customFieldInfo.customParam==null){
top.$.alert("請聯系應用設計師,在表單編輯器中設置電子發票控件的屬性映射!");
return;
}
var opts = {
type: 0,
applicationCategory: 66,
maxSize: '',
isEncrypt: '',
popupTitleKey: '',
attachmentTrId: 'Att',
takeOver: 'false',
firstSave: 'true',
quantity: 1,
extensions: 'pdf'
};
//文件上傳成功后
self.uploadFile(opts, function (fileurls, atts) {
if (atts && atts.length > 0) {
var att = atts;
att[0].refefence = self.getData.attachmentInfo.baseAttachmentInfo.reference;
att[0].subReference = self.getData.attachmentInfo.baseAttachmentInfo.subReference;
self.att = att;
box.querySelector(".cap4-images__items").innerHTML = '<div style="cap4-images__it"><a title="'+atts[0].filename+'" style="display: block;max-height: 20px;" target="myFormIframe" href="/seeyon/fileDownload.do?method=download&fileId='+self.att[0].fileUrl+'&v='+self.att[0].v+'&createDate='+self.att[0].createDate+'&filename='+encodeURIComponent(self.att[0].filename)+'">'+atts[0].filename+'</a><div class="cap4-images__close"><i class="icon CAP cap-icon-guanbi delete"></i></div></div>';
//icon botton
box.querySelector(".cap4-images__cnt").style.background='';
box.querySelector('.upload').style.display = "none";
self.aId = atts[0].id;
box.querySelector('.delete').addEventListener('click', function () {
var field = box.querySelector(".cap4-images__items");
field.removeChild(field.childNodes[0]);
box.querySelector('.upload').style.display = "";
box.querySelector(".cap4-images__cnt").style.background=(messageObj.isNotNull=='1'?'#fef0d0':'');
if (self.adaptation.backfillFormAttachment) {
var attachmentData = [
{
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
updateRecordId: self.getData.recordId || '',
handlerMode: 'delete',
fieldName: self.getData.id,
deleteAttchmentData: [self.aId]
}
];
//backfillFormAttachment(向數據庫刪除需要刪除的內容)
self.adaptation.backfillFormAttachment(JSON.parse(JSON.stringify(attachmentData)),privateId);
}
});
//rest
var content = self.messageObj.formdata.content;
var params = {
formId: content.contentTemplateId,
rightId: content.rightId,
fieldName: getData.id,
fileId: atts[0].fileUrl,
masterId: content.contentDataId,
subId: getData.recordId || '0'
};
var process = top.$.progressBar({
text: "解析中"
});
$.ajax({
type: 'get',
url: (_ctxPath ? _ctxPath : '/seeyon') + "/rest/cap4/formEinvoice/parseEinvoiceFileAndFillBack?" + self.parseParam(params),
dataType: 'json',
contentType: 'application/json',
beforeSend: function () {
},
success: function (res) {
process.close();
if (res.success || res.code == "0") {
if (self.adaptation.backfillFormControlData) {
var backfillData = [];
var backfillItem = {
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
updateData: {},
updateRecordId: ''
}
if (self.countProperties(res.data) === 0) {
return;
} else {
var key = [];
for (var k in res.data) {
if (k.split('_').length > 1) {
key.push(k.split('_')[1]);
}
}
if (key.length == 0) {
backfillItem.updateData = res.data;
backfillData.push(backfillItem);
} else {
if (self.removal(key).length === 1) {
if (self.countProperties(res.data) === key.length) {
backfillItem.updateData = res.data;
backfillItem.updateRecordId = key[0];
backfillData.push(backfillItem);
} else {
var mTable = {};
var sTable = {};
for (p in res.data) {
if (p.split('_').length === 2 && p.split('_')[1] === key[0]) {
sTable[p] = res.data[p];
} else {
mTable[p] = res.data[p];
}
}
backfillData.push({
updateData: mTable,
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
});
backfillData.push({
updateData: sTable,
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
updateRecordId: key[0]
});
}
} else {
var len = removal(key);
for (var i = 0; i <= len.length; i++) {
var tableData = {};
for (p in res.data) {
if (p.split('_').length === 2) {
if (p.split('_')[1] === len[i]) {
tableData[p] = res.data[p];
}
} else {
if (i === len.length) {
tableData[p] = res.data[p];
}
}
}
backfillData.push({
updateData: tableData,
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
updateRecordId: len[i] || ''
});
}
}
}
}
if (backfillData && backfillData.length > 0) {
for (var i = 0; i < backfillData.length; i++) {
for (var k in backfillData[i].updateData) {
if (k.split('_').length > 1) {
backfillData[i].updateData[k.split('_')[0]] = backfillData[i].updateData[k];
delete backfillData[i].updateData[k];
} else {
backfillData[i].updateData[k] = backfillData[i].updateData[k];
}
}
}
}
//backfillFormControlData(回填電子發票相關聯的其它控件的值)
self.adaptation.backfillFormControlData(backfillData, privateId);
}
if (self.adaptation.backfillFormAttachment) {
var attachmentData = [
{
tableName: self.formMessage.tableName,
tableCategory: self.formMessage.tableCategory,
updateRecordId: self.getData.recordId || '',
handlerMode: 'add',
fieldName: self.getData.id,
addAttchmentData: self.att
}
]
//backfillFormAttachment(向數據庫添加新建的內容)
self.adaptation.backfillFormAttachment(JSON.parse(JSON.stringify(attachmentData)), privateId);
}
} else {
}
},
complete: function () {
process.close();
},
error:function (errorMsg) {
var retObj = $.parseJSON(errorMsg.responseText);
top.$.alert(retObj.message);
process.close();
}
});
}
});
});
}
}
self.download = function () {
}
self.removal = function (arr) {
var res = [];
var json = {};
for (var i = 0; i < arr.length; i++) {
if (!json[arr[i]]) {
res.push(arr[i]);
json[arr[i]] = 1;
}
}
return res;
}
self.countProperties = function (obj) {
var count = 0;
for (var property in obj) {
if (Object.prototype.hasOwnProperty.call(obj, property)) {
count++;
}
}
return count;
}
//定義上傳文件接口
self.uploadFile = function (options, callback) {
self.parseParam = function (param, key) {
var paramStr = "";
if (param instanceof String || param instanceof Number || param instanceof Boolean) {
paramStr += "&" + key + "=" + encodeURIComponent(param);
} else {
$.each(param, function (i) {
var k = key == null ? i : key + (param instanceof Array ? "[" + i + "]" : "." + i);
paramStr += '&' + self.parseParam(this, k);
});
}
return paramStr.substr(1);
}
options.CSRFTOKEN = top.CsrfGuard.getToken();
var url = (_ctxPath ? _ctxPath : '/seeyon') + '/fileUpload.do?callMethod=_dinvoiceUploadFileCallback&' + self.parseParam(options);
getCtpTop().addattachDialog = null;
getCtpTop().addattachDialog = getCtpTop().$.dialog({
title: $.i18n('fileupload.page.title'),
transParams: {
parentWin: window
},
url: url,
width: 400,
height: 250
});
window._dinvoiceUploadFileCallback = function (att, repeat) {
var atts = null;
atts = att;
if (atts && atts.instance.length) {
//處理一下返回的數據,拼接一下url,統一修改
atts.instance.forEach(function (item) {
var opts = {
createDate: item.createDate.split(' ')[0],
fileId: item.fileUrl
};
var url = (_ctxPath ? _ctxPath : '/seeyon') + '/fileUpload.do?method=showRTE&type=image&showType=big&' + self.parseParam(opts);
item.src = url; //增加src屬性,寫入url
});
callback(null, atts.instance);
} else {
callback(null, []);
}
}
}
self.initParams(options);
}
App.prototype.initParams = function (res) {
var self = this;
var adaptation = res.adaptation;
var url_prefix = res.url_prefix;
var privateId = res.privateId;
self.formMessage = res.formMessage;
self.getData = res.getData;
var dataObj = adaptation.childrenGetPrivateMessage(privateId);
self.messageObj = adaptation.childrenGetData(privateId);
self.appendChildDom(adaptation, self.messageObj, privateId, res.getData);
// 監聽是否數據刷新
adaptation.ObserverEvent.listen('Event' + privateId, function () {
//self.messageObj = adaptation.childrenGetData(privateId);
//self.appendChildDom(adaptation, self.messageObj, privateId);
});
};
return App;
});
~~~

**更多運行態相關說明請見:[自定義控件-前端](http://mall.seeyon.com/#/custom-custom)**
### 2.2.3 自定義控件在CAP4報表查詢中有哪些屬性
自定義控件在CAP4報表查詢中,需要由開發控件的人自己定義控件有哪些屬性,屬性由com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrlReportInfo進行描述,需要在自定義控件實現類的canShowInReport方法中進行屬性的定義和添加,具體屬性設置如下表:
| 屬性名 | 說明 |
| :----------------: | :---------------------------------------------: |
| enableQuery | 是否在查詢中使用,如果為false的話不在查詢中顯示 |
| enableDisplayField | 是否支持數據字段設置 |
| enableFormulaField | 是否支持公式列 |
| enableSort | 是否支持排序設置 |
| enableFilterField | 是否支持篩選條件-普通條件 |
以關聯項目自定義控件為例:
```java
@Override
public FormFieldCustomCtrlReportInfo canShowInReport() {
FormFieldCustomCtrlReportInfo reportInfo = new FormFieldCustomCtrlReportInfo();
reportInfo.setEnableQuery(true);
reportInfo.setEnableDisplayField(true);
reportInfo.setEnableSort(true);
reportInfo.setEnableFilterField(true);
return reportInfo;
}
```
我們在查詢配置中數據字段設置/篩選條件設置,就可以看到這個自定義控件[**項目關聯1**]:


### 2.2.4 自定義控件查詢前端運行態渲染開發
#### 2.2.4.1查詢列表
對于如何將數據庫字段中存放的類似{"projectName":"項目1","id":"-8312506844008854456"}這種格式的數據在查詢列表中正常顯示出來,有兩種方案:
- 覆寫接口中的convertCtrlValue方法將字段值轉換為顯示值,查詢前端默認會以文本的格式顯示出來;
- 覆寫接口中的convertCtrlValue方法和getRenderInfo4Run方法,并開發自己的列表插槽,具體可參看[自定義控件(插槽)](自定義控件(列表插槽).md)

#### 2.2.4.2 篩選條件
自定義控件條件支持的操作類型
| 操作類型 | 說明 |
| :------: | :------------------: |
| Equal | 等于 |
| NotEqual | 不等于 |
| Like | 字符串模糊匹配包含 |
| NotLike | 字符串模糊匹配不包含 |
對于篩選條件的開發示例可以具體參考[自定義控件(篩選條件)](自定義控件(篩選條件).md)


篩選后結果如下:

## 3、[自定義控件DEMO下載](http://mall.seeyon.com/help/Public/dev/static/zip/formEinvoice.zip)(本demo以表單電子發票這個自定義控件為例):
* apps目錄中是移動端相關文件源碼,自定義控件移動端采用H5技術;
* src中是server端以及pc設置態相關源碼;
- 概要
- 技術介紹
- 框架與環境
- vue開發
- 開發規范
- 前端開發規范
- 總體原則
- HTML規范
- HTML&css規范
- vue編碼規范
- Javascript規范
- 后端開發規范
- cap4
- 自定義控件
- 前端2.0(PC+移動)
- PC前端
- 后端
- 移動端
- 移動端接口
- 低版本協同升級到V5 8.0適配說明
- 自定義按鈕
- 自定義按鈕(無流程)
- 自定義控件(列表插槽)
- 自定義按鈕(篩選條件)
- 低版本協同升級到V5 8.0適配說明
- 門戶空間
- 門戶與欄目掛載
- 欄目開發及流程說明
- 頁面模板
- 客開通路及插件體系
- 表單設計器擴展配置
- 使用步驟
- 配置說明
- 事件API
- Demo示例
- 運行態客開通路
- 插件使用步驟
- 插件接口
- 事件接口
- 鉤子相關接口
- 表單操作接口
- Demo示例
- 插件機制
- 表單運行態接口(舊)
- 白名單插件
- 版本記錄
- vue組件庫
- 開發指南
- 開發文檔規范
- 業務組件介紹
- 業務組件
- table組件
- 分頁組件
- title組件
- 統計排隊組件
- code組件
- 條件篩選
- 批量導入
- 上傳Excel
- 批量更新
- 批量刷新
- UI組件
- 按鈕組件
- 復選組件
- 取色器組件
- 示例組件
- 水平選擇組件
- 選圖標組件
- 提示組件
- 單選組件
- 搜索組件
- 選擇組件
- 穿梭框組件
- 標簽組件
- 文本組件
- 樹組件
- 驗證組件
- 菜單組件
- iframe組件
- toolbar
- 統計組件
- 餅圖
- 柱狀圖
- 圖標
- 業務關系開發指南
- 自定義觸發
- 自定義關聯
- 后端API
- 更新表單數據緩存
- 發起表單流程
- 取得指定表單PDF或截圖
- 無流程批量添加
- 無流程批量刪除
- 無流程批量更新
- 無流程批量導出
- 客開培訓文檔
- Vue基礎培訓
- Vue實戰培訓
- Vue進階培訓
- VueCLI3培訓
- cap3
- 自定義控件
- 后端
- 移動端
- 前端編譯
- 表單運行態接口
- 協同云