这一节加入模块自定义字段,并根据这些字段生成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', 100 }); break; case 'Datetime': Ext.apply(field, { xtype: 'datecolumn', align: 'center', 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', 110 }); break; case 'Float': Ext.apply(field, { align: 'center', xtype: 'numbercolumn', 110 }); break; case 'Percent': Ext.apply(field, { align: 'center', xtype: 'numbercolumn', 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>'); } } });
文件的结构: