跟我一起学extjs5(14--模块字段和Grid列的定义[1])
这一节加入模块自定义字段,并根据这些字段生成model。然后再定义grid中的分组和列。从这一切开始真正进入到了模块自定义的节奏当中,代码的复杂度和技巧性也大大提高。先从模块字段的自定义开始。先看一下ModuleModel.js中加入的新的定义:
/** * 模块的数据模型 */ Ext.define('app.view.module.ModuleModel', { extend : 'Ext.app.ViewModel', alias : 'viewmodel.module', // 在开发过程中我先用设定好的值放于data中,等以后自定义的时候,data里的值都是从后台取得的 // 所有数据库里的字段,我都以tf_开头,只是为了表示这是从后台读取过来的 data : { tf_moduleId : '1010', // 模块ID号:一个数字的ID号,可以根据此ID号的顺序将相同分组的模块放在一块。 tf_ModuleGroup : '工程管理',// 模块分组:模块分到哪个组里,比如说业务模块1、业务模块2、系统设置、系统管理等。 tf_moduleName : 'Global', // 模块标识:系统中唯一的模块的标识 tf_title : '工程项目',// 模块名称:能够描述此模块信息的名称。 tf_glyph : 0xf0f7, // 图标字符值 tf_shortname : null,// 模块简称:如果名称过长,有些地方可以用简称来代替。 tf_englishName : null,// 模块英文名称:万一要制作英文版,可以用英文名称。 tf_englishShortName : null, // 模块英文简称:可以用作生成编码字段。 tf_description : null,// 模块描述: tf_remark : null, // 备注: // 下面还有若干字段未加入,以后用到的时候再加入 tf_primaryKey : 'tf_id', // 主键 tf_nameFields : 'tf_name', // 可用于描述记录的字段 // 此模块的自定义字段,此处先用手工定义,以后换成从数据库中自动取得 tf_fields : [{ tf_fieldId : 10100010, // 此字段的id值,所有的字段都是保存在一字段表中,这是主键值 tf_fieldName : 'tf_id',// 字段名 tf_title : '序号',// 字段描述 tf_fieldType : 'Integer', // 字段类型 tf_isHidden : true, // 是否是隐藏字段 tf_fieldGroup : '工程基本信息' // 字段分组 // 是否是隐藏字段 }, { tf_fieldId : 10100020, tf_fieldName : 'tf_name', tf_title : '工程项目名称', tf_fieldType : 'String', tf_fieldLen : 50, tf_isRequired : true, // 是否是必添项 tf_fieldGroup : '工程基本信息' }, { tf_fieldId : 10100030, tf_fieldName : 'tf_code', tf_title : '工程项目编码', tf_fieldType : 'String', tf_fieldLen : 20, tf_isRequired : true, tf_fieldGroup : '工程基本信息' // 字段分组 }, { tf_fieldId : 10100040, // 加入一个整型字段 tf_fieldName : 'tf_squaremeter', tf_title : '建筑面积', tf_fieldType : 'Integer', tf_unitText : '平米', // 字段单位 tf_fieldGroup : '工程附加信息', tf_allowSummary : true // 可以对此字段进行小计 }, { tf_fieldId : 10100050, // 加入一个金额字段 tf_fieldName : 'tf_budget', tf_title : '投资总额', tf_fieldType : 'Double', tf_isMoney : true, // 此字段是一个金额字段 tf_fieldGroup : '工程附加信息', tf_allowSummary : true }, { tf_fieldId : 10100060, // 加入一个百分比字段 tf_fieldName : 'tf_rjl', tf_title : '容积率', tf_fieldType : 'Percent', tf_fieldGroup : '工程附加信息' }, { tf_fieldId : 10100070, // 加入一个日期 tf_fieldName : 'tf_startDate', tf_title : '计划开工时间', tf_fieldType : 'Date', tf_fieldGroup : '工程附加信息' }, { tf_fieldId : 10100080, // 加入一个日期 tf_fieldName : 'tf_endDate', tf_title : '计划竣工时间', tf_fieldType : 'Date', tf_fieldGroup : '工程附加信息' }, { tf_fieldId : 10100090, // 加入一个布尔字段 tf_fieldName : 'tf_isValid', tf_title : '是否通过验收', tf_fieldType : 'Boolean', tf_fieldGroup : '工程附加信息' }, { tf_fieldId : 10100100, // 加入一个数值字段 tf_fieldName : 'tf_m3', tf_title : '工程方量', tf_fieldType : 'Double', tf_fieldGroup : '工程附加信息' }], // 模块的grid方案,可以定义多个方案 tf_gridSchemes : [{ tf_schemeOrder : 10, tf_schemeName : 'Grid方案1', // 第一个grid方案 // 表头分组 tf_schemeGroups : [{ tf_gridGroupId : 1, // id号 tf_gridGroupOrder : 10, // 表头分组序号 tf_gridGroupName : '工程项目基本信息', tf_isShowHeaderSpans : true, // 是否显示分组 tf_isLocked : true, // 是否锁定此分组 // 每一个表头分组下面的字段 tf_groupFields : [{ tf_gridFieldOrder : 10, tf_fieldId : 10100020, // 工程项目名称字段 tf_columnWidth : 200 }, { tf_gridFieldOrder : 20, tf_fieldId : 10100030, // 工程项目编码字段 tf_columnWidth : 120 }] }, { tf_gridGroupOrder : 20, // 表头分组序号 tf_gridGroupName : '工程项目附加信息', tf_isShowHeaderSpans : true, // 是否显示headerspan tf_isLocked : false, // 是否锁定此分组 // 每一个表头分组下面的字段 tf_groupFields : [{ tf_gridFieldOrder : 10, tf_fieldId : 10100040 // 建筑面积 }, { tf_gridFieldOrder : 20, tf_fieldId : 10100050 // 投资总额 }, { tf_gridFieldOrder : 30, tf_fieldId : 10100060 // 容积率 } , { tf_gridFieldOrder : 40, tf_fieldId : 10100070 // 计划开工时间 } , { tf_gridFieldOrder : 50, tf_fieldId : 10100080 // 计划竣工时间 } , { tf_gridFieldOrder : 60, tf_fieldId : 10100090, // 是否通过验收 tf_columnWidth : 80 }, { tf_gridFieldOrder : 70, tf_fieldId : 10100100 // 工程方量 }] }] }] }, // 根据字段id,找到字段相应的定义 getFieldDefine : function(fieldId) { var result = null; Ext.Array.each(this.data.tf_fields, function(field) { if (field.tf_fieldId == fieldId) { result = field; return false; } }); return result; } })
在上面代码中在data里面加入了tf_fields这个数组,这个数组下的每个元素都是一个字段,比如:
{ tf_fieldId : 10100020, tf_fieldName : 'tf_name', tf_title : '工程项目名称', tf_fieldType : 'String', tf_fieldLen : 50, tf_isRequired : true, // 是否是必添项 tf_fieldGroup : '工程基本信息' }上面就定义了一个工程项目名称的字段,包括id号,字段名称,类型,长度,是否是必添项,分组信息等等。在这个工程项目模块中,我加入了各种类型的字段,有字符型,整型,浮点型,金额型,日期型,布尔型,都可以演示了看看效果。
字段定义好了以后,需要能够根据字段的定义自动生成model。下面我编了一个model工厂的类,能根据ModuleModel中的定义自动生成一个model。先在module目录下面建立一个新的目录factory ,然后在这个下面建立一个文件 ModelFactory.js,内容如下:
/** * 根据module的数据来生成模块的model */ Ext.define('app.view.module.factory.ModelFactory', { // 静态变量或函数 statics : { // 生成module的model,传入的数据是ModelModel中的data getModelByModule : function(moduleModel) { console.log('moduleModel'); console.log(moduleModel); var module = moduleModel.data; return Ext.define('app.model.' + module.tf_moduleName, { extend : 'Ext.data.Model', module : module, idProperty : module.tf_primaryKey, // 设置模块model的主键 nameFields : module.tf_nameFields, // 设置模块model的名称字段 fields : this.getFields(module), // 设置字段 // 取得主键值 getIdValue : function() { return this.get(this.idProperty); }, // 取得当前记录的名字字段 getNameValue : function() { if (this.nameFields) return this.get(this.nameFields); else return null; } }); }, // 根据字段字义数组来生成model中的各个字段 getFields : function(module) { var fields = []; for (var i in module.tf_fields) { var fd = module.tf_fields[i]; var field = { name : fd.tf_fieldName, title : fd.tf_title, type : this.getTypeByStr(fd.tf_fieldType) }; if (field.type == 'string') { field.useNull = true; field.serialize = this.convertToNull; } if (field.type == 'date') { field.dateWriteFormat = 'Y-m-d'; // 设置日期字段的读写格式 field.dateReadFormat = 'Y-m-d'; } if (field.type == 'datetime') field.dateReadFormat = 'Y-m-d H:i:s'; fields.push(field); } return fields; }, // 将java中的数据类型转换成extjs5的字段类型 getTypeByStr : function(str) { switch (str) { case 'String' : return 'string'; case 'Boolean' : return 'boolean'; case 'Integer' : return 'int'; case 'Date' : return 'date'; case 'Datetime' : return 'date'; case 'Double' : case 'Float' : case 'Percent' : return 'float'; default : return 'string'; } }, // 如果是空字符串,返回null convertToNull : function(v) { return v ? v : null; } } });
上面是生成model的一个类。下面再继续看最上面的ModuleModel.js中data下的另一个属性:tf_gridSchemes,这个属性下的数据是用来定义grid的分组和列,tf_schemeGroups 是定义了若干个分组,tf_groupFields定义这些分组下面的具体的列。这些数据以后也都是保存在数据库中的,只要建立三张表,一张放方案名称,一张放分组名牌,一张放分组下面的字段定义。对表进行修改,就能把数据传给前台,重新展示就是新的一个grid的列方案。并且方案可以制订多个,在界面中方全的切换。
下面的任务就是要为grid 生成columns,与上面生成model的方法相同,建立一个ColumnFactory.js程序,用来根据配置文件生成columns。
/** * 用于生成Grid的Columns的类 */ Ext.define('app.view.module.factory.ColumnsFactory', { statics : { getColumns : function(moduleModel, schemeOrderId) { var scheme = moduleModel.get('tf_gridSchemes')[0]; // 取得第一个grid的方案 var columns = []; for (var i in scheme.tf_schemeGroups) { var sg = scheme.tf_schemeGroups[i]; // 是否需要显示分组 var isgroup = sg.tf_isShowHeaderSpans; var group = { gridGroupId : sg.tf_gridGroupId, text : sg.tf_gridGroupName, locked : sg.tf_isLocked, //flex : 1, columns : [] } for (var j in sg.tf_groupFields) { var gf = sg.tf_groupFields[j]; var fd = moduleModel.getFieldDefine(gf.tf_fieldId); var field; if (fd.tf_isHidden) continue; field = this.getColumn(gf, fd, moduleModel); field.locked = sg.tf_isLocked; if (isgroup) { this.canReduceTitle(group, field); group.columns.push(field); } else columns.push(field); } if (isgroup) { this.canReduceTitle(group, field); columns.push(group); } } console.log(columns); return columns; }, // 看看分组名称是不是 下面column 的开头,如果是开头的话,并且columntitle 后面有内容,就把 // 相同的部分截掉 canReduceTitle : function(group, field) { if (field.text.indexOf(group.text) == 0) { field.text = field.text.slice(group.text.length).replace('(', '') .replace(')', '').replace('(', '').replace(')', ''); if (field.text.indexOf("<br/>") == 0) field.text = field.text.slice(5); } }, /** * 根据groupField,fieldDefine的定义,生成一个column的定义 */ getColumn : function(gf, fd, module) { // console.log(fd); var ft = fd.tf_title.replace(new RegExp('--', 'gm'), '<br/>'); if (fd.behindText) ft += '<br/>(' + fd.behindText + ')'; var field = { filter : {}, maxWidth : 800, gridFieldId : gf.tf_gridFieldId, // 加上这个属性,用于在列改变了宽度过后,传到后台 sortable : true, text : ft, dataIndex : fd.tf_fieldName } switch (fd.tf_fieldType) { case 'Date' : Ext.apply(field, { xtype : 'datecolumn', align : 'center', width : 100 }); break; case 'Datetime' : Ext.apply(field, { xtype : 'datecolumn', align : 'center', width : 130 }); break; case 'Boolean' : field.xtype = 'checkcolumn'; field.stopSelection = false; field.processEvent = function(type) { if (type == 'click') return false; }; break; case 'Integer' : Ext.apply(field, { align : 'center', xtype : 'numbercolumn', tdCls : 'intcolor', format : '#' }); break; case 'Double' : Ext.apply(field, { align : 'center', xtype : 'numbercolumn', width : 110 }); break; case 'Float' : Ext.apply(field, { align : 'center', xtype : 'numbercolumn', width : 110 }); break; case 'Percent' : Ext.apply(field, { align : 'center', xtype : 'numbercolumn', width : 110 }) break; case 'String' : break; default : break; } if (fd.tf_allowSummary) { Ext.apply(field, { hasSummary : true, summaryType : 'sum' }) } if (gf.tf_columnWidth > 0) field.width = gf.tf_columnWidth; else if (gf.tf_columnWidth == -1) { field.flex = 1; field.minWidth = 120; } return field; }, /** * 对于当前模块的name字段,加粗显示 */ nameFieldRenderer : function(val, rd, model, row, col, store, gridview) { return filterTextSetBk(store, '<strong>' + val + '</strong>'); } } });
文件的结构: