需求
实现可编辑表格和表单的动态配置,即动态获取页面显示的元素并通过配置实现对后端读取数据匹配字段、label名称、元素类型、关联数据源、匹配方式、状态、默认值等属性的控制。
需注意:
1、对于有联动和有特殊操作的复杂逻辑元素匹配为静态方式,用字段key控制仍然使用“静态页面模版”的代码逻辑。
2、配置静态方式的元素依然可以匹配一些动态属性,如宽度、状态、label等
3、根据配置的状态,动态添加表单验证
名词解析
1、动态模版:获取到json类型的页面元素及表现形式集合,通过它可以组成新dom。
2、静态模版:固定在原模版页里的dom结构,匹配固定元素、样式、后端字段接口、和逻辑操作
3、匹配字段key:原静态页面里元素的id;及保存或读取时后台对应的字段名
4、元素类型:多为form元素类型(input、select等)、自定义的获取信息组件(穿梭框、复杂表格选择器等)
5、关联数据源:元素为下拉列表时列表读取接口地址
6、匹配方式:动态,使用动态模版中提供的各种条件构成元素和交互逻辑;静态,使用原静态页中的元素和交互逻辑
处理分析
1、拿到模版json,为空,(代码没有动态模版)使用原静态模版的dom;不为空,遍历json里的属性生成新的dom结构;
2、遍历新的dom结构,对比json中的属性匹配相应的样式和交互逻辑,生成新模版页
3、读取数据,显示到新的页面
4、编写新的验证规则
首先配置方式分为两种:
1、可编辑的表格
2、表单
3、纯展示类的表格和表单(这个比较简单,不写了)
实施步骤
一、可编辑表格
静态模版是easyui的edatagrid,动态模版延用组件,只是将columns的配置改为动态模版获取。
1、编写函数获取模版
function getPageItemListForEdit(resId, firstObj, resName, oldColumns) {
var resultsTemp = null;
$.ajax({
url: 'pageModel.queryPageItemListByResId.action?resId=' + resId,
type: 'POST',
async: false,
success: function (retData) {
var itemList = retData.data;
resultsTemp = getColumnArrEdit(itemList, firstObj, resId, oldColumns);
}
});
return resultsTemp;
}
2、处理模版信息生成新的columns,根据匹配方式,自动适配动态模版和原静态模版元素
function getColumnArrEdit(itemList, firstObj, resId, oldList) {
var align = getAlign();
var itemArr = new Array();
if (itemList != null && itemList.length > 0) {
blArrayIndex = 0;
if (firstObj != null) {
itemArr.push(firstObj);
}
for (var i = 0; i < itemList.length; i++) {
var oldObj = null;
var obj = itemList[i];
var code = obj.ITEM_CODE;//PAGE_PROPERTY_ID
try{
if (oldList && (typeof oldList == 'object')) {
oldObj = oldList.find(itemOld => itemOld.field === obj.ITEM_CODE);
}
}catch (e) {
}
var name = obj.PAGE_ITEM_NAME;
var width = obj.WIDTH;
var MUTI_TEXT = obj.MUTI_TEXT;
var state = obj.STATE;//0编辑,1必填,2浏览
var defaultVal = obj.DEFAULT_VAL;
var visitable = obj.VISITABLE;
if (visitable == '1') {//不可见
continue;
}
if (width == null || width == 0) {
width = 100;
}
var type = obj.PAGE_ITEM_TYPE;
var codeRes = obj.CODE_RES;
var itemObj = new Object();
var editor = new Object();
var options = new Object();
var buttons = $.extend([], $.fn.datebox.defaults.buttons);
buttons.splice(1, 0, {
text: '清空',
handler: function (target) {
$(target).datebox('setValue', '').datebox('hidePanel');
}
});
itemObj.POSITION_TYPE = obj.POSITION_TYPE;
itemObj.APPLICATION_METHOD = obj.APPLICATION_METHOD;
itemObj.field = code;
itemObj.title = name;
itemObj.align = align;
itemObj.width = width;
itemObj.sortable = true;
//添加(表头提示)
itemObj.info = obj.INFO;
itemObj.hasDefaultVal = defaultVal;
itemObj.editor = editor;
itemObj.editor.options = options;
if (itemObj.APPLICATION_METHOD && itemObj.APPLICATION_METHOD != '1' && oldObj) {
if ('1' == state) {
itemObj.editor = oldObj.editor;
itemObj.editor.options ? (itemObj.editor.options.required = true) : itemObj.editor.options = {required: true};
} else if ('0' == state) {
itemObj.editor = oldObj.editor;
itemObj.editor.options ? (itemObj.editor.options.required = false) : itemObj.editor.options = {required: false};
}
itemObj.formatter = oldObj.formatter;
} else {
if (type == '2') {//日期型
if ('1' == state || '0' == state) {
itemObj.editor.type = 'datebox';
itemObj.editor.options.buttons = buttons;
if ('1' == state) {
itemObj.editor.options.required = true;
itemObj.editor.options.editable = false;
} else {
itemObj.editor.options.required = false;
itemObj.editor.options.editable = false;
}
}
itemObj.formatter = function (value, rowData, rowIndex) {
if (value != null && value != '' && value.length >= 10) {
value = value.substring(0, 10);
}
return value;
}
}
else if (type == '4') {//下拉列表
codeBlArray.push(codeRes);
if ('1' == state || '0' == state) {
itemObj.editor.type = 'combobox';
if ('1' == state) {
itemObj.editor.options.required = true;
} else {
itemObj.editor.options.required = false;
}
itemObj.editor.options.data = getPcodeListEdit(codeBlArray[blArrayIndex]);
itemObj.editor.options.valueField = 'id';
itemObj.editor.options.textField = 'text';
itemObj.editor.options.editable = false;
}
}
else if (type == '5') {
if ('1' == state || '0' == state) {
itemObj.editor = {type: 'numberbox', options: {required: ('1' == state ? true : false)}};
itemObj.formatter = function (value, rowData, rowIndex) {
var val = !value || value == 'null' || value == '' ? '' : parseInt(value);
return val;
}
}
}
else if (type == '7') {
if ('1' == state || '0' == state) {
itemObj.editor = {
type: 'numberbox',
options: {required: ('1' == state ? true : false), precision: 1}
};
itemObj.formatter = function (value, rowData, rowIndex) {
var val = !value || value == 'null' || value == '' ? '' : Number(value).toFixed(1);
return val;
}
}
}
else {
if ('1' == state || '0' == state) {
itemObj.editor.type = 'textbox';
if ('1' == MUTI_TEXT) {
itemObj.editor.type = 'textarea';
itemObj.formatter = function (value, rowData, rowIndex) {
if (value != null) {
value = myReplaceCommon(value, '\n', '<br>');
return value;
}
return "";
}
}
if ('1' == state) {
itemObj.editor.options.required = true;
} else {
itemObj.editor.options.required = false;
}
}
}
}
itemArr.push(itemObj);
}
}
itemArrMy = itemArr;
return itemArr;
}
3、使用easyui的edatagrid组件初始化表格,并读取数据赋值
注意:
(1)排查静态模版中特殊处理逻辑的列是否都设置成了静态模式,并且与后端字段id匹配
(2)遍历静态模版columns,排查是否有动态模版中不存在的,用于存储数据的隐藏列,将其添加到新columns
$('#tableId').edatagrid({
url:'tableData_url',
pagination:false,
pagePosition: 'top',
rownumbers:true,
fitColumns : false,
fit:true,
striped : true,
loadMsg : ' ',
sortOrder : 'desc',
remoteSort : false,
singleSelect: false,
autoSave : true,
queryParams: '',
frozenColumns : getFrozenColumn(bean),
columns:bean.columns,
onLoadSuccess:function(){
//原逻辑
},
onBeginEdit:function(index,row){
//原逻辑
},
onEndEdit: function(index,row,changes){
//原逻辑
}
});
function getFrozenColumn(bean, add) {
if (bean.frozenColumns != undefined) {
return bean.frozenColumns;
}
bean.frozenColumns = new Array();
bean.frozenColumns[0] = new Array();
var frozen = getConfFrozenColumnNum();//接口获取冻结列数
try {
if (add) {
frozen = frozen + add;
} else {
frozen = frozen + 1; //加一个序号列
}
} catch (e) {
}
if (frozen >= bean.columns[0].length) {
return null;
}
for (var i = 0; i <= frozen; i++) {
var col = bean.columns[0].shift();
bean.frozenColumns[0].push(col);
}
return bean.frozenColumns;
}
4、编写提交验证
$(“#tableId”).edatagrid('validateRow’,editRowIndex);
二、表单页
1、获取模版,生成新的dom
let modelItems = await getEditPageItemListByIdN(resId,"1");
if(modelItems!=null && modelItems.length>0){
//设置输入控件的easyui样式
await setInputBoxStyle(modelItems);
//设置只读属性
await setReadOnly(modelItems);
}
function getEditPageItemListByIdN(resId, flag) {
var f = '';
try {
if (flag) {
f = '1';
}
} catch (e) {
}
var resultsTemp = null;
$.ajax({
url: 'temp_url',
type: 'POST',
async: false,
success: function (retData) {
var html = "";
var itemList = retData.data;
resultsTemp = retData.data;
if (itemList && itemList.length > 0) {
for (var i = 0; i < itemList.length; i++) {
var obj = itemList[i];
var code = obj.ITEM_CODE;
var id = obj.PAGE_PROPERTY_ID;
var visitable = obj.VISITABLE;
var sort = obj.P_SORT;
var isTowColumn = obj.IS_TWOCOLMN;
var state = obj.STATE;
var name = obj.PAGE_ITEM_NAME;
var mode = obj.APPLICATION_METHOD;
var itemType = obj.PAGE_ITEM_TYPE;
var MUTI_TEXT = obj.MUTI_TEXT;
var ROW_NUM = obj.ROW_NUM;
var INFO = obj.INFO;
try {
var cla = "rowItem" + f;
if (isTowColumn == '1') {//占两列
cla = "rowItemTowColumn" + f;
}
var sta = false;
if (state == '1') {//必填,可编辑(只读 另做处理)
sta = true;
}
if (visitable == '1') {
html += "<div class=\"" + cla + "\" style=\"display:none\">";
} else {
html += "<div class=\"" + cla + "\" >";
}
if (INFO && INFO != '') {
var helpIcon = "<span class='toolBox-Diy' style='left:-3px; top:3px'>" +
"<img class='icon-help-htable ' src='../../base/css/img3/help.png'/>" +
'<div class="tooltip tooltip-bottom" style="display: none; margin-top: 3px;"><div class="tooltip-content">' + INFO + '</div><div class="tooltip-arrow-outer" style="border-bottom-color: rgb(221, 221, 221);"></div><div class="tooltip-arrow" style="border-bottom-color: rgb(255, 255, 255);"></div></div>'
"</span>";
html += " <div class=\"content\">" + name + ":" + helpIcon + " </div>";
} else {
html += " <div class=\"content\">" + name + ":</div>";
}
if ('1' == MUTI_TEXT) {
var hh = 24 * ROW_NUM;
html += " <div class=\"element" + f + "\">";
html += " <input id=\"" + id + "\" style=\"height: 23px;\" name=\"" + id
+ "\" data-mode=\"" + mode + "\" data-options=\"multiline:true, required:" + sta + "\" style=\"height:" + hh + "px\"/>";
html += " </div>";
html += "</div>";
} else {
html += " <div class=\"element" + f + "\">";
html += " <input id=\"" + id + "\" style=\"height: 23px;\" name=\"" + id
+ "\" data-mode=\"" + mode + "\" data-options=\"required:" + sta + "\" />";
html += " </div>";
html += "</div>";
}
//码表类型,默认值
} catch (e) {
}
}
$("#globalColumnDiv").html(html);
}
setTimeout(function () {
try {
$('.toolBox-Diy').hover(function () {
$(this).find('.tooltip-bottom').show();
var left = $(this).offset().left - 5;
$(this).find('.tooltip-arrow-outer').css('left', '10px')
$(this).find('.tooltip-arrow').css('left', '10px')
$(this).find('.tooltip-bottom').css('left', left)
setTimeout(function () {
$('.tooltip-bottom').hide();
}, 10000);
})
$('.toolBox-Diy').mouseleave(function () {
$(this).find('.tooltip-bottom').hide();
})
} catch (e) {
}
}, 100);
}
});
return resultsTemp;
}
2、处理生成新的domtree初始化form
function setInputBoxStyle(itemList, resId, isEditCombobox) {
if (itemList == null || itemList.length <= 0) {
return;
}
for (var i = 0; i < itemList.length; i++) {
var obj = itemList[i];
var mode = obj.APPLICATION_METHOD;
var id = obj.PAGE_PROPERTY_ID;
var type = obj.PAGE_ITEM_TYPE;
var codeRes = obj.CODE_RES;//码表源
var MUTI_TEXT = obj.MUTI_TEXT;
var ROW_NUM = obj.ROW_NUM;
if (mode == '2') {
$('#' + id).textbox();
//$('#' + id).parent().replaceWith();
} else if (mode == '3') {
$('#' + id).textbox();
//$('#' + id).parent().replaceWith();
} else if (mode == '4') {
$('#' + id).textbox();
//$('#' + id).parent().replaceWith();
} else if (mode == '5') {
$('#' + id).textbox();
//$('#' + id).parent().replaceWith();
$('#' + id).textbox();
if (obj.DEFAULT_VAL) $('#' + id).textbox('setValue', obj.DEFAULT_VAL);
} else {
if (type == '1') {//文本型
$('#' + id).textbox();
if ("1" == MUTI_TEXT) {
var hh = 23 * ROW_NUM;
$('#' + id).textbox({height: hh});
if (obj.DEFAULT_VAL) $('#' + id).textbox('setValue', obj.DEFAULT_VAL);
}
} else if (type == '2') {//日期型
//$('#'+id).datebox() ;
$('#' + id).datebox({
editable: false, buttons: buttons
});
} else if (type == '3') {//日期时间型
//$('#'+id).datetimebox() ;
$('#' + id).datetimebox({
editable: false
});
} else if (type == '5') {//数值型
$('#' + id).numberbox();
if (obj.DEFAULT_VAL) $('#' + id).numberbox('setValue', Number(obj.DEFAULT_VAL));
} else if (type == '4') {//下拉列表
if (codeRes != null && codeRes !== '') {
loadCodeDataToCombobox(id, codeRes, obj.DEFAULT_VAL);
}
} else if (type == '6') {//下拉树
$('#' + id).combotree();
if (codeRes != null && codeRes != '') {
loadCodeDataToCombotree(id, codeRes, obj.DEFAULT_VAL)
}
} else if (type == '7') {
$('#' + id).numberbox({
precision: 1,
formatter: function (value) {
// 将数字转换为字符串
let strValue = value;
// 检查是否包含小数点
if (strValue.includes('.')) {
// 去掉尾部的 0
strValue = strValue.replace(/0+$/, '');
// 如果去掉尾部 0 后小数点后面没有数字了,去掉小数点
strValue = strValue.replace(/\.$/, '');
}
return strValue;
}
});
} else if (type == '8') {
$('#' + id).numberbox({
precision: 2,
formatter: function (value) {
// 将数字转换为字符串
let strValue = value;
// 检查是否包含小数点
if (strValue.includes('.')) {
// 去掉尾部的 0
strValue = strValue.replace(/0+$/, '');
// 如果去掉尾部 0 后小数点后面没有数字了,去掉小数点
strValue = strValue.replace(/\.$/, '');
}
return strValue;
}
});
}
}
}
}
3、 静态化元素单独处理(使用静态模版逻辑重新初始化元素)
(1)排查静态模版中的用于存储值的隐藏元素,添加到新的模版
(2)排查新模版中的静态元素,重新按静态模版的方式初始化
if(modelItems.find(item=>item.PAGE_PROPERTY_ID==='SCHEME_TYPE' && (item.APPLICATION_METHOD && item.APPLICATION_METHOD !== '1'))){
loadCodeDataToComboboxForIntel('SCHEME_TYPE','ZNTJFA');
}
4、 读取数据赋值
setInputBoxValue(modelItems, formDetail);
function setInputBoxValue(itemList, resultObj) {
if (itemList == null || itemList.length <= 0 || resultObj == null) {
return;
}
for (var i = 0; i < itemList.length; i++) {
var obj = itemList[i];
var id = obj.PAGE_PROPERTY_ID;
var type = obj.PAGE_ITEM_TYPE;//信息项类型(1.文本型,2.日期型,3.日期时间型,4.码表型)
var codeRes = obj.CODE_RES;//码表源
var columnName = obj.ITEM_CODE;
if (type == '1') {//文本型
try {
$('#' + id).textbox('setValue', resultObj[columnName]);
} catch (e) {
}
;
} else if (type == '2') {//日期型
try {
$('#' + id).datebox('setValue', resultObj[columnName]);
} catch (e) {
}
;
} else if (type == '3') {//日期时间型
try {
$('#' + id).datetimebox('setValue', dateStrFormat(resultObj[columnName]));
} catch (e) {
}
;
} else if (type == '5') {//数值型
try {
$('#' + id).numberbox('setValue', resultObj[columnName]);
} catch (e) {
}
;
} else if (type == '4') {//码表型
try {
$('#' + id).combobox('setValue', resultObj[columnName]);
} catch (e) {
try {
$('#' + id).combotree('setValue', resultObj[columnName]);
} catch (e) {
}
;
}
;
} else if (type == '6') {//码表--下拉树型
try {
$('#' + id).combotree('setValue', resultObj[columnName]);
} catch (e) {
try {
$('#' + id).combobox('setValue', resultObj[columnName]);
} catch (e) {
}
;
}
;
} else if (type == '7') {
try {
$('#' + id).numberbox('setValue', resultObj[columnName]);
} catch (e) {
try {
$('#' + id).textbox('setValue', resultObj[columnName]);
} catch (e) {
}
}
} else if (type == '8') {
try {
$('#' + id).numberbox('setValue', resultObj[columnName]);
} catch (e) {
try {
$('#' + id).textbox('setValue', resultObj[columnName]);
} catch (e) {
}
}
} else {
try {
$('#' + id).textbox('setValue', resultObj[columnName]);
} catch (e) {
}
}
}
}
5、编写提交验证
$(“formID”).form('validate')
并对有特殊逻辑验证的元素进行单独验证
总结
这样就可以实现大部分页面的动态配置,而不需要设置大量静态模版了~
注意:
1、已有静态页改造时,需要核对已有的元素id和动态模版id,确保一致性
2、特殊处理的元素保持静态,匹配“静态元素”,表单页注意需要重新初始化元素
3、验证时注意动态模版可能增减元素的特点,灵活处理验证,比较简单的方式是用trycatch,或使用表单自带form('validate');但对特殊逻辑还是要感觉模版是否存在元素判断是否验证