# CAP4自定義控件 - 移動端
* * *
CAP4移動端表單自定義控件基本開發文檔
CAP4表單自定義控件開發源自于PC端自定義控件,為了盡量保持一致開發,CAP4移動端自定義控件開發規范與[PC端自定義控件開發規范](http://mall.seeyon.com/help/Public/dev/index.html?0.589103825843704#custom-custom)基本保持一致。
## 1\. 開發規范
### 1.1. 自定義控件項目結構
自定義控件項目前端可按照如下目錄結構組織資源(目錄結構及命名非強制要求),可根據實際情況增刪目錄。
~~~
─my-widget 組件資源存放的目錄名
├─js js資源
├─css css資源
├─icon 圖標資源
├─index.html html資源
~~~
### 1.2. 自定義控件生命周期
自定義控件必須實現init及destroy接口,負責自定義控件的渲染及銷毀
* 自定義控件渲染
~~~
a. 表單渲染時調用自定義控件實現的init接口來渲染自定義控件
b. 調用init接口時主動表單會傳遞以下參數
c. 可以根據需要動態/按需加載控件所需的其他資源
myWidget.init({
adaptation, // 表單操作代理
url_prefix, // 控件資源目錄存放位置,如1.1.中的my-widget
privateId // 控件的標識符
})
~~~
* 自定義控件銷毀
~~~
表單會在某些情況(如視圖切換/保存新建/保存復制)下需要調用自定義控件的destroy接口銷毀自定義控件
myWidget.destroy(privateId)
~~~
### 1.3. 自定義控件開發
* 自定義控件命名空間
~~~
a. CAP4表單在渲染自定義控件時會使用定義的命名控件獲取自定義控件接口,用于管理自定義控件生命周期。
b. 命名空間建議以"cap4_custom_widget_"作為前綴,后綴根據控件自身語義命名
c. 命名空間目前默認注冊到window對象,請盡量按照建議做法避免命名沖突
~~~
*示例:*
~~~javascript
var scopeName = "cap4_custom_widget_my_widget_name";
var widgetImpl = {};
window[scopeName] = widgetImpl;
~~~
* 自定義控件渲染及事件交互
~~~
a. 組件必須實現init及destroy方法
b. init方法用于組件初始化(如組件渲染、事件綁定、更新渲染等)
c. destroy方法用于組件銷毀(如解除事件綁定、釋放內存)
d. 以上兩個接口均由表單發起調用
~~~
*示例:*
~~~javascript
var scopeName = "cap4_custom_widget_my_widget_name";
var widgetImpl = {
init: function(options) {
// 自定義初始化/渲染/事件綁定
},
destroy: function(widgetId) {
// 自定義控件銷毀
}
};
window[scopeName] = widgetImpl;
~~~
* 獲取自定義控件數據
~~~
通過init接口傳入的adaptation.childrenGetData方法獲取控件數據
var field = adaptation.childrenGetData(privateId)
field = {
attachmentInfo : 自定義控件附件信息,
attrs : 自定義控件屬性樣式,
auth : 控件權限 追加(add) 編輯(edit) 瀏覽(browse) 隱藏(hide),
ctrlBorderStyle :,
ctrlTitleStyle: ,
ctrlType : 控件類型,
customFieldInfo : 自定義控件的自定義控件信息,
display : 控件名,
enums : 控件的枚舉信息,
fieldLength : 字段長度,
fieldType : 字段類型,
formType: 表單類型 (formmain : 主表, formson : 明細表),
recordId: 明細表的行ID,
id: 字段ID(這里是字段名),
inputType: 控件類型,
isInCalculate: 是否計算(‘1’: 計算, ‘0’: 不計算),
isInCondition: ,
isNotNull: 是否必填,
placeHolder: 提示信息,
relation: ‘關聯信息’,
relationData: {
imgShow: 是否顯示關聯圖標,
viewThrough :"1"//是否可穿透
toMasterDataId:"2222"//穿透的數據id 無流程指的是表單數據id 有流程就是summeryId
},
showValue: 顯示數據信息,
value:參與計算的值,
valueId :數據庫存儲值
formdata : { // 表單信息 (這之前都是當前控件信息)
content : 表單的基礎信息(包括表單的權限Id, 表單Id的等),
formmains : 表單的主表數據,
formsons : 表單的明細表數據,
model : 明細表是頁簽模式還是平鋪模式(平鋪模式是default),
allData : 整個表單的所有數據
}
}
~~~
* 監聽自定義控件數據變化
~~~
a. 表單計算等可能引起自定義控件數據
b. 使用init接口傳入的ObserverEvent.listen方法監聽自定義控件數據變化并根據情況作出更新
adaptation.ObserverEvent.listen('Event' + privateId, function(newField) {
// 更新你的控件
})
~~~
* 自定義控件回填/更新表單數據
| 方法 | 說明 | 參數 | 參數格式 | 參數說明 |
| --- | --- | --- | --- | --- |
| backfillFormControlData | 用于回填控件數據 | (payload, privatedId) | (Object/Array, String) | payload: 回填的數據, privatedId 當前控件的唯一標識ID(入口方法傳入) |
| backfillFormAttachment | 用于回填附件數據 | (payload, privatedId) | (Object/Array, String) | payload: 回填的數據, privatedId 當前控件的唯一標識ID(入口方法傳入) |
~~~javascript
// 回填數據示例 - 數組
payload = [
{
tableName : String,
tableCategory : String,
updateData : {
field0002 : {
value : 'your value'
...
},
field0003 : {
value : 'your value'
...
}
},
updateRecordId : String
}
]
// 回填數據示例 - 對象
payload = {
tableName : String,
tableCategory : String,
updateData : {
field0002 : {
value : 'your value'
...
},
field0003 : {
value : 'your value'
...
}
},
updateRecordId : String
}
~~~
* 自定義控件調用表單方法
~~~
可以通過adaptation.backCallApi(name, callback, payload)方法在需要時主動調用表單公開的方法。
name: String 方法的名稱
callback: Function 方法調用完成之后的回調(可選)
payload: Any 表單方法接受的參數,由對應的方法決定
~~~
*支持的方法列表*
| 名稱 | 參數 | 說明 |
| --- | --- | --- |
| presave | { needCheckRule: '0', needDataUnique: '0', needSn: '0' } | 表單預提交, 將用戶填寫的表單數據寫入后端緩存 |
*調用示例*
~~~javascript
adaptation.backCallApi(
'presave',
function() {
console.log('表單預提交成功');
},
{ needSn: '1' }
)
~~~
## 2\. 開發示例
~~~
下面以實現一個按鈕點擊彈出"Hello CAP4!"例子演示如何開發自定義控件
~~~
*示例:*
~~~javascript
(function () {
// >>> step1. 定義控件唯一標識(命名空間)
var scopeName = "field_5902128098173592526";
// >>> step2. 實現自定義控件渲染及交互邏輯
var MyWidgetImpl = function (options) {
this.customWidgetType = scopeName;
this._create(options);
};
MyWidgetImpl.prototype = {
constructor: MyWidgetImpl,
_create: function (options) {
this._options = options;
this._widgetId = options.privateId;
this._widgetProxy = options.adaptation;
this._wrapper = document.getElementById(this._widgetId);
this._field = this._widgetProxy.childrenGetData(this._widgetId);
this._tunnelId = 'Event' + this._widgetId;
if (!this._wrapper) return console.warn('沒有找到組件渲染的容器');
this.clicked = 0;
this._update();
console.log(this._widgetProxy);
},
_update: function (field) {
if (field) this._field = field;
this._destroy();
this._buildRendering();
this._postCreate();
},
_buildRendering: function () {
var elemId = 'section-' + this._widgetId;
var btnId = 'section-' + this._widgetId + '-btn';
var tpl = '<section id="section-' + elemId + '">' +
'<button id="' + btnId + '" style="display:block;border:1px solid blue;border-radius:5px;margin:8px 16px;padding:5px;line-height:24px;;text-align:center">' + this._field.display +
'clicked ' + this.clicked + '次</button>' +
'</section>';
this._wrapper.innerHTML = tpl;
this._el = this._wrapper.querySelector('#' + elemId);
this._btnEl = this._wrapper.querySelector('#' + btnId);
},
_postCreate: function () {
this._unbindTap = this._bind(this._btnEl, 'tap', this.onClick.bind(this));
this._unbindUpdate = this._listen(this._tunnelId, this._update.bind(this));
},
_destroy: function () {
this._unbindTap && this._unbindTap();
this._unbindUpdate && this._unbindUpdate();
this._el = this._btnEl = null;
},
_bind: function (el, evt, cb) {
el.addEventListener(evt, cb, false);
return function () {
el.removeEventListener(evt, cb, false);
}
},
_listen: function(evt, cb) {
var hub = this._widgetProxy.ObserverEvent;
hub.listen(evt, cb);
return function() {
hub.remove(evt, cb);
}
},
onClick: function () {
this.clicked += 1;
this._btnEl.innerHTML = this._field.display +
'clicked ' + this.clicked + '次';
alert('Hello CAP4!');
this._update();
}
};
// >>> step3. 實現開發規范約定的init及destroy接口
var myWidgets = {};
// 組件初始化
MyWidgetImpl.init = function (options) {
var widget = new MyWidgetImpl(options);
myWidgets[ widget._widgetId ] = widget;
};
// 組件銷毀
MyWidgetImpl.destroy = function (widgetId) {
var widget = myWidgets[ widgetId ];
if (widget) widget._destroy();
delete myWidgets[ widgetId ];
};
// >>> step4. 注冊組件實現到命名控件
window[ scopeName ] = MyWidgetImpl;
})();
~~~
*運行效果:*

## 3\. 注意事項
* 自定義控件有動態加載資源時,要防護重復加載資源
* 由于數據變化等引起自定義控件需要更新時,要做好事件清理解綁,防止重復綁定事件
* 如果需要使用到cmp組件,請參考[cmp組件文檔](http://open.seeyon.com/seeyon/cmp2.0/doc/)
* 未列出內容,待后續更新補充
* * *
*End*
- 概要
- 技術介紹
- 框架與環境
- 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
- 自定義控件
- 后端
- 移動端
- 前端編譯
- 表單運行態接口
- 協同云