EXT的奇妙之旅

来源:互联网 发布:水牌制作软件 编辑:程序博客网 时间:2024/03/29 10:07

         初次知道EXT还是通过看一个同事以前做的项目,当时他们用EXT做了一个桌面版的系统,看到就一下被吸引了,心想:"原来WEB版也可以做的这么酷",后来就买了一本有关EXT的书学习,发现EXT有好多面向对象的设计,给我的感觉就是每个JS文件就详单与Java中的类文件一样,学了一段时间之后就想着尝试自己做一个系统玩玩儿,做了一段时间发现,EXT其实还是挺难学的,后来就一直希望能在自己的项目中应用,但是一直没有机会实战,再后来由于项目使用easyui,就又投身到easyui的研究中,就放下了Ext的学习,在不经意间发现Ext已经跟新了好几个版本,最近趁项目运维阶段不是很忙,抽时间用EXT5做了一个系统,以前的东西忘得也差不多了,也是在网上找了一些资料,学习了一下,项目才算初步成型,不过还有很长的路要走啊,希望自己能坚持下去吧。奋斗


项目----项目管理系统

目的是想着一个公司肯定有好多系统,想OA,还有好多商业系统,然后自己就想能不能对这些项目进行一下统一管理呢,这样的话,如果项目里遇到问题还可以参考其他系统的实现,从而实现公司内部技术的共享。


目前项目的成果:

  1. 下图显示的是进入项目的主页的整体布局


点击---》项目信息--》进入项目信息页面



点击人员信息---》进入人员信息界面



点击用户管理---》进入用户管理界面



其他的操作类似就不一一列举了,目前的界面还有和数据库做交互


项目中使用的技术:

前端:Extjs5.0

后台:SpringMVC

服务器:Tomcat

数据库:Mysql

开发工具:eclipse

所有jar包的依赖通过Maven管理


Extjs的搭建过程:


  1. 新建一个Maven项目,过程如下:

      


点击next:



点击next



点击完成即可


2.生成Ext所需环境

需要使用sencha cmd,具体可以参考:http://lingyi.red/using-sencha-cmd-with-ext-js-5


EXT主要代码实现如下:


Main.js


/** * This class is the main view for the application. It is specified in app.js as * the "autoCreateViewport" property. That setting automatically applies the * "viewport" plugin to promote that instance of this class to the body element. *  * TODO - Replace this content of this view to suite the needs of your * application. */Ext.define('webapp.view.main.Main', {extend : 'Ext.container.Container',requires : [ 'webapp.view.main.MainController','webapp.view.main.MainModel' ],uses : [ 'webapp.view.module.Module' ],// 设置图标字体文件,只有设置了以后才能用glyph属性initComponent : function() {Ext.setGlyphFontFamily('FontAwesome');//this.getViewModel().set('monetary.value','tenthousand');this.callParent();},xtype : 'app-main',uses : ['webapp.view.main.layout.Center', 'webapp.view.main.layout.Top', 'webapp.view.main.layout.Bottom','webapp.view.main.layout.AccordionMainMenu' ],controller : 'main',viewModel : {type : 'main'},layout : {type : 'border'},items : [ {xtype : 'maintop',region : 'north'}, {xtype : 'mainbottom',region : 'south'},{xtype : 'mainmenuaccordion',region : 'west', // 左边面板width : 250,split : true}, {region : 'center',xtype : 'maincenter'} ]});

MainController.js

/** * This class is the main view for the application. It is specified in app.js as * the "autoCreateViewport" property. That setting automatically applies the * "viewport" plugin to promote that instance of this class to the body element. *  * TODO - Replace this content of this view to suite the needs of your * application. */Ext.define('webapp.view.main.MainController', {extend : 'Ext.app.ViewController',requires : [ 'Ext.window.MessageBox' ],uses : [ 'webapp.view.module.Module' ],alias : 'controller.main',// 选择了主菜单上的菜单后执行onMainMenuClick : function(menuitem) {var maincenter = this.getView().down('maincenter');//获取tab组件var tab = maincenter.getComponent(menuitem.moduleName);//如果组件没有创建,则添加,否则不在添加,目的就是防止用户重复点击导航栏if (tab == null) {maincenter.setActiveTab(maincenter.add({id : menuitem.moduleName,xtype : 'modulepanel',moduleName : menuitem.moduleName,closable : true,reorderable : true}));}},setting : function() {alert(1);}});


MainModel.js


/** * This class is the view model for the Main view of the application. */Ext.define('webapp.view.main.MainModel', {extend : 'Ext.app.ViewModel',alias : 'viewmodel.main',constructor : function() {Ext.log('MainModel constructor');var me = this;// 这一句是关键,如果没有的话,this还没有初始化完成,下面的Ext.apply(me.data,....)这句就会出错this.callParent(arguments);// 同步调用取得系统参数Ext.Ajax.request({url : 'application.do',//加载模块信息async : false, // 同步success : function(response) {var text = response.responseText;// 将字段串转换成本地变量var applicationInfo = Ext.decode(text, true);// 把从后台传过来的参数加入到data中去Ext.apply(me.data, applicationInfo);}});},data : {name : '系统导航',// 系统信息system : {name : '项目管理系统',iconUrl : ''},// 单位信息service : {company : 'XXXX',copyright : 'XXXX'},// 系统菜单的定义,这个菜单可以是从后台通过ajax传过来的systemMenu : [ {text : '项目管理', // 菜单项的名称icon : '', // 菜单顶的图标地址glyph : 0,// 菜单项的图标字体的数值expanded : true, // 在树形菜单中是否展开description : '', // 菜单项的描述items : [ {text : '项目信息', // 菜单条的名称module : 'Global', // 对应模块的名称icon : '', // 菜单条的图标地址glyph : 0xf0f7// 菜单条的图标字体}, {text : '人员信息',module : 'Worker',icon : '',glyph : 0xf02e} ]}, {text : '知识管理',expanded : true,items : [ {text : '问题管理',module : 'Problem',glyph : 0xf02d}, {text : '技术分享',module : 'Shared',glyph : 0xf03a} ]}, {text : '系统管理',expanded : true,items : [ {text : '用户管理',module : 'userMg',glyph : 0xf02d}, {text : '角色管理',module : 'Role',glyph : 0xf03a}, {text : '权限管理',module : 'Authority',glyph : 0xf022}, {text : '菜单管理',module : 'Resource',glyph : 0xf0d6} ]}],},getModuleDefine : function(moduleId) {var result = null;Ext.Array.each(this.get('tf_Modules'), function(module) {if (module.tf_moduleId == moduleId + ''|| module.tf_moduleName == moduleId) {result = module;return false;}})return result;}// TODO - add data, formulas and/or methods to support your view});

Top.js


/** * 页面顶部设计 */Ext.define('webapp.view.main.layout.Top', {extend : 'Ext.toolbar.Toolbar',alias : 'widget.maintop', // 定义了这个组件的xtype类型为maintopstyle : 'background-color : #cde6c7',uses : [ 'webapp.ux.ButtonTransparent' ],defaults : {xtype : 'buttontransparent'},items : [ {xtype : 'image',bind : { // 数据绑定到MainModel中data的ystem.iconUrlhidden : '{!system.iconUrl}', // 如果system.iconUrl未设置,则此image不显示src : '{system.iconUrl}' // 根据system.iconUrl的设置来加载图片}}, {xtype : 'label',bind : {text : '{system.name}' // text值绑定到system.name},style : 'font-size : 20px; color : blue;'}, {text : '注销',glyph : 0xf011}, {text : '设置',glyph : 0xf013,style:'right:auto; left:1200px;top:0px',handler:'setting'} ]});

Bottom.js


/** * 页面底部实现 */Ext.define('webapp.view.main.layout.Bottom', {extend : 'Ext.toolbar.Toolbar',alias : 'widget.mainbottom',uses : [ 'webapp.ux.ButtonTransparent' ],defaults : {xtype : 'buttontransparent'},// 背景颜色style : 'background-color : #DFEAF2;',items : [ {bind : {text : '服务单位:{service.company}'}}, {bind : {text : '©{service.copyright}'}} ]});

Center.js


/** * 系统界面的主区域,是一个tabpanel,可以有多个tab页面,用来放置各个模块。 */Ext.define('webapp.view.main.layout.Center',{extend : 'Ext.tab.Panel',alias : 'widget.maincenter',closeAction : 'hide',autoDestroy : true,tabPosition : 'top',plugins : [{ptype : 'tabclosemenu',closeAllTabsText : '关闭所有',closeOthersTabsText : '关闭其他',closeTabText : '关闭',extraItemsTail : ['-',{text : '可关闭',itemId : 'canclose',checked : true,hideOnClick : false,handler : function(item) {item.ownerCt.tabPanel.tab.setClosable(item.checked);}},'-',{text : '登录时自动打开',itemId : 'autoopen',checked : false,hideOnClick : false,handler : function(item) {if (item.checked)Jfok.system.addModuleToAutoOpen(item.ownerCt.tabPanel.moduleName);elseJfok.system.deleteModuleToAutoOpen(item.ownerCt.tabPanel.moduleName);}},'-',{xtype : 'fieldcontainer',items : {xtype : 'numberfield',fieldLabel : '最多打开Tab数',itemId : 'maxtab',width : 156,value : 8,maxValue : 20,minValue : 3,listeners1 : {change : function(field,newValue, oldValue) {Jfok.system.setMaxTab(newValue);}}}} ],listeners : {beforemenu : function(menu, tabPanel) {// 此插件有bug,需要加入这个参数menu.tabPanel = tabPanel;if (tabPanel.tab.reorderable) {menu.down('#canclose').setChecked(tabPanel.tab.closable);menu.down('#canclose').enable();} else {menu.down('#canclose').setChecked(false);menu.down('#canclose').disable();}// 如果是有parentFilter的模块,那么自动打开的菜单条隐掉// ,上面的'-'也隐掉menu.down('#autoopen').setVisible(!tabPanel.parentModuleName);menu.down('#autoopen').previousSibling().setVisible(!tabPanel.parentModuleName);menu.down('#autoopen').setChecked(false);// Jfok.system// .isModuleAutoOpen(tabPanel.moduleName));menu.down('#maxtab').setValue(8);// Jfok.system.getMaxTab());}}}, Ext.create('Ext.ux.TabReorderer') ],})


折叠菜单的js


/** * 折叠菜单实现 */Ext.define('webapp.view.main.layout.AccordionMainMenu', {extend : 'Ext.panel.Panel',alias : 'widget.mainmenuaccordion',title : '系统菜单',glyph : 0xf0c9,layout : {type : 'accordion',animate : true},viewModel : 'main',initComponent : function() {this.items = [];var menus = this.getViewModel().get('systemMenu');for ( var i in menus) {var menugroup = menus[i];var accpanel = {menuAccordion : true,xtype : 'panel',title : menugroup.text,bodyStyle : {padding : '10px'},layout : 'fit',dockedItems : [ {dock : 'left',xtype : 'toolbar',items : []} ],glyph : menugroup.glyph};for ( var j in menugroup.items) {var menumodule = menugroup.items[j];accpanel.dockedItems[0].items.push({moduleName:menumodule.module,xtype : 'buttontransparent',text : this.addSpace(menumodule.text, 12),glyph : menumodule.glyph,handler : 'onMainMenuClick'});}this.items.push(accpanel);}this.callParent(arguments);},addSpace : function(text, len) {console.log(text.length);var result = text;for ( var i = text.length; i < len; i++) {result += ' ';}return result;}});

Module.js


/** * 一个模块的主控界面的容器,用来安放各个模块控件以及协调他们之间的关系 */Ext.define('webapp.view.module.Module', {extend : 'Ext.panel.Panel',alias : 'widget.modulepanel',requires : [ 'webapp.view.module.ModuleController','webapp.view.module.ModuleModel','webapp.view.module.factory.ModelFactory' ],uses : [ 'webapp.view.module.region.Navigate','webapp.view.module.region.Grid', 'webapp.view.module.user.Grid','webapp.view.module.region.WorkerGrid','webapp.view.module.grid.Problem','webapp.view.module.grid.Shared', 'webapp.view.module.grid.Role','webapp.view.module.grid.Authority','webapp.view.module.grid.Resource' ],referenceHolder : true,controller : 'module',bind : {title : '{tf_title}' // 这个绑定是有效的,可以根据ModuleModel中的值来设置title},layout : 'border', // 模块采用border布局initComponent : function() {// 从MainModel中取得当前模块的定义数据,包括字段和各种设置的信息var mainmodel = this.up('app-main').getViewModel();this.module = mainmodel.getModuleDefine(this.moduleName);var viewmodel = new Ext.create('webapp.view.module.ModuleModel', {// 将该模块的定义信息传递给本模块的viewModelmodule : this.module});this.setViewModel(viewmodel);this.glyph = this.getViewModel().get('tf_glyph'); // 由于上面的glyph的bind无效,因此需要在这里加入glyph的设置this.model = webapp.view.module.factory.ModelFactory.getModelByModule(this.module);this.items = [ {xtype : this.moduleName, // 模块的grid显示区域region : 'center'//store : this.store} ]this.callParent();}})

ModuleModel.js


Ext.define('webapp.view.module.ModuleModel', {extend : 'Ext.app.ViewModel',alias : 'viewmodel.module',constructor : function() {Ext.log('module constructor');// 这一句是关键,如果没有的话,this还没有初始化完成,下面的Ext.apply(me.data,....)这句就会出错this.callParent(arguments);// 取得MainModel.js中取得的这个模块的的初始化信息Ext.apply(this.data, this.module)},// 在开发过程中我先用设定好的值放于data中,等以后自定义的时候,data里的值都是从后台取得的// 所有数据库里的字段,我都以tf_开头,只是为了表示这是从后台读取过来的data : {tf_moduleId : null, // 模块ID号:一个数字的ID号,可以根据此ID号的顺序将相同分组的模块放在一块。tf_ModuleGroup : null,// 模块分组:模块分到哪个组里,比如说业务模块1、业务模块2、系统设置、系统管理等。tf_moduleName : null, // 模块标识:系统中唯一的模块的标识tf_title : null,// 模块名称:能够描述此模块信息的名称。tf_glyph : null, // 图标字符值tf_shortname : null,// 模块简称:如果名称过长,有些地方可以用简称来代替。tf_englishName : null,// 模块英文名称:万一要制作英文版,可以用英文名称。tf_englishShortName : null, // 模块英文简称:可以用作生成编码字段。tf_description : null,// 模块描述:tf_remark : null,// 备注:// 模块的grid方案,可以定义多个方案tf_gridSchemes : [],// 模块的form方案,可以定义多个方案tf_formSchemes : [],selectedNames : '' // 选中的记录的names显示在title上},formulas : {// 模块grid方案的选择Combo是否显示gridSchemeHidden : function(get) {return this.get('tf_gridSchemes').length <= 1;}},// 根据字段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;}})

ModuleController.js


Ext.define('webapp.view.module.ModuleController', {extend : 'Ext.app.ViewController',requires : [ 'Ext.MessageBox', 'Ext.window.Toast' ],alias : 'controller.module',init : function() {console.log('modulecontroller.init')}})

ModelFactory.js


/** * 根据module的数据来生成模块的model */Ext.define('webapp.view.module.factory.ModelFactory',{statics : {getModelByModule : function(module) {var model;//在define之前需要判断model是否已经定义过,否则会报错if(!Ext.isDefined('webapp.view.model.'+module.tf_moduleName)){model= Ext.define('webapp.view.model.'+ module.tf_moduleName,{extend : 'Ext.data.Model',module : module,idProperty : module.tf_primaryKey,nameFields : module.tf_nameFields,titleTpl : module.tf_titleTpl,titleTemplate : null,fields : this.getFields(module),proxy : {type : 'rest',batchActions : true,extraParams : {moduleName : module.tf_moduleName},api : {// 在这里加rest/是因为在web.xml中// <url-pattern>/rest/*</url-pattern>这一句,spring会根据rest// 后面的参数去进行匹配//read : 'rest/module/fetchdata.do',//update : 'rest/module/update.do',//create : 'rest/module/create.do',//destroy : 'rest/module/remove.do'},actionMethods : {create : 'POST',read : 'GET',update : 'PUT',destroy : 'DELETE'},reader : {type : 'json',root : 'records',totalProperty : 'totalCount'},writer : {type : 'json',writeRecordId : true,writeAllFields : false// 没有修改过的字段不加入到update和delete的json中去},listeners : {exception : function(proxy,response,operation) {// 将出错信息加到proxy中去,传递到store的sync中显示出错信息,显示后将此属性删除proxy.errorInfo = Ext.decode(response.responseText,true);// 如果出错信息解析出错,则加入一个缺省的if (!proxy.errorInfo)proxy.errorInfo = {resultCode : -1,errorMessage : '未知原因:'+ response.responseText}}}},getTitleTpl : function() {if (!this.titleTemplate) {if (this.titleTpl)this.titleTemplate = new Ext.Template(this.titleTpl);elsethis.titleTemplate = new Ext.Template('{'+ this.nameFields+ '}');}return this.titleTemplate.apply(this.getData())},// 此条记录是否可以修改canEdit : function() {if (this.module.tf_hasAuditing&& this.get('tf_auditinged'))return false;if (this.module.tf_hasApprove&& this.get('tf_shNowCount') > 0)return false;return true;},// 此条记录是否可以进行操作canOperate : function() {if (this.module.tf_hasAuditing&& this.get('tf_auditinged'))return false;return true;},// 此条记录是否可以删除canDelete : function() {if (this.module.tf_hasAuditing&& this.get('tf_auditinged'))return {canDelete : false,message : '【'+ this.getTitleTpl()+ '】已进行过审核,不允许进行删除操作!'};if (this.module.tf_hasApprove&& this.get('tf_shNowCount') > 0)return {canDelete : false,message : '【'+ this.getTitleTpl()+ '】正在审批或已经审批完成,不允许进行删除操作!'};return true;},// 取得主键值getIdValue : function() {return this.get(this.idProperty);},// 取得当前记录的名字字段getNameValue : function() {if (this.nameFields)return this.get(this.nameFields);elsereturn null;}});}return model;},// String("String"), Boolean("Boolean"),// Integer("Integer"),// Date("Date"), Double("Double"), Float("Float");// PercentgetFields : 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 (fd.tf_fieldType == 'Date') {field.dateWriteFormat = 'Y-m-d';field.dateReadFormat = 'Y-m-d';}if (fd.tf_fieldType == 'Datetime')field.dateReadFormat = 'Y-m-d H:i:s';field.tf_haveAttachment = fd.tf_haveAttachment;fields.push(field);}return fields;},getTypeByStr : function(str) {console.log(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 'Money':case 'Percent':return 'float';default:return 'string';}},// 如果是空字符串,返回nullconvertToNull : function(v) {return v ? v : null;}}});


下面就是一个展示表格的js,实现都差不多就不一一列举了,以用户管理的表格为例


Ext.define('webapp.view.module.user.Grid', {extend : 'Ext.grid.Panel',alias : 'widget.userMg',uses : [ 'webapp.view.module.region.GridToolbar' ],bind : {title : '{tf_title}' // 数据绑定到ModuleModel中的tf_title},dockedItems :{xtype : 'gridtoolbar', // 按钮toolbardock : 'top',grid : this,store:this.myStore},// 自定义字段的还没有做,先放几个固定的columns : [ {dataIndex : 'id',text : '用户ID',width : 250}, {dataIndex : 'userName',text : '用户姓名'}, {dataIndex : 'email',text : '用户邮箱'} , {dataIndex : 'createDate',text : '创建时间'}, {dataIndex : 'createId',text : '创建ID'} , {dataIndex : 'status',text : '状态'}    ],})// var myStore = Ext.create('Ext.data.Store', {//     model: 'User',//     proxy: {//         type: 'ajax',//         url: '/user/list.do',//         reader: {//             type: 'json',//             rootProperty: 'root'//         }//     },//     autoLoad: true// });

接下来就是一部分java代码的实现


Module.java 用于动态生成点击导航栏菜单是生成项目的Module


/** * 模块实体 *  */@Entity@Table(name = "t_module")@SuppressWarnings("serial")@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)public class Module extends BaseEntity {    /**  */    private static final long serialVersionUID = 1L;    @Id    private Integer tf_moduleId;    private String tf_ModuleGroup;    private String tf_moduleName;    private String tf_title;    private String tf_glyph;    private String tf_shortName;    public Integer getTf_moduleId() {        return tf_moduleId;    }    public void setTf_moduleId(Integer tf_moduleId) {        this.tf_moduleId = tf_moduleId;    }    public String getTf_ModuleGroup() {        return tf_ModuleGroup;    }    public void setTf_ModuleGroup(String tf_ModuleGroup) {        this.tf_ModuleGroup = tf_ModuleGroup;    }    public String getTf_moduleName() {        return tf_moduleName;    }    public void setTf_moduleName(String tf_moduleName) {        this.tf_moduleName = tf_moduleName;    }    public String getTf_title() {        return tf_title;    }    public void setTf_title(String tf_title) {        this.tf_title = tf_title;    }    public String getTf_glyph() {        return tf_glyph;    }    public void setTf_glyph(String tf_glyph) {        this.tf_glyph = tf_glyph;    }    public String getTf_shortName() {        return tf_shortName;    }    public void setTf_shortName(String tf_shortName) {        this.tf_shortName = tf_shortName;    }}

ApplicationController控制器,用于加载Module数据

@Controllerpublic class ApplicationController {    @Resource(name = "applicationService")    private ApplicationService applicationService;    private Map jsonMap = new HashMap();    @RequestMapping("/application.do")    @ResponseBody    public Map<String, List<Module>> findModules() {        jsonMap.put("tf_Modules", applicationService.findAll());        return jsonMap;    }    public Map getJsonMap() {        return jsonMap;    }    public void setJsonMap(Map jsonMap) {        this.jsonMap = jsonMap;    }}

ApplicationService


public interface ApplicationService {    /**     * @return     */    List<Module> findAll();}

ApplicationServiceImpl


@Service("applicationService")@Transactionalpublic class ApplicationServiceImpl implements ApplicationService{    @Resource(name="applicationDao")    private ApplicationDao applicationDao;        /**     * @return     * @see com.pms.service.ApplicationService#findAll()     */    @Override    public List<Module> findAll() {        // TODO Auto-generated method stub        return applicationDao.findAll();    }}

ApplicationDao


@Repository("applicationDao")public class ApplicationDao extends BaseDao<Module> {    public List<Module> findAll() {        return findByhql(" from Module where 1=1 ");    }}

BaseDao


public class BaseDao<E extends BaseEntity> {    @PersistenceContext    private EntityManager entityManager;    public EntityManager getEntityManager() {        return entityManager;    }    public void setEntityManager(EntityManager entityManager) {        this.entityManager = entityManager;    }    /**     * 添加     *      * @param entity     * @return     */    public E save(E entity) {        if (entity != null) {            entityManager.persist(entity);        }        return entity;    }    /**     * 更新     *      * @param entity     * @return     */    public E update(E entity) {        entity = entityManager.merge(entity);        return entity;    }    /**     * 删除     *      * @param entity     */    public void delete(E entity) {        entityManager.remove(entity);    }    /**     * 根据ID查询     *      * @param entityClass     * @param id     * @return     */    public E findById(Class<E> entityClass, Integer id) {        return entityManager.find(entityClass, id);    }    /**     * 根据Hql查询     *      * @param hql     * @return     */    public List<E> findByhql(String hql) {        Query query = entityManager.createQuery(hql);        return query.getResultList();    }}

         目前就写到这个程度了,由于本人也是一个码农,只有抽时间继续完成其他功能,权限打算使用Shiro实现,而且对Ext也是似懂非懂,需要边学边做,而且这次使用SpringMVC也是初次尝试,因为开发项目用的都是SSH,之所以用SpringMVC就是出于学习的目的。


最后需要感谢:点击打开链接 的作者,因为这个项目的实现是参照该作者的讲解实现,而且有些代码也是基于作者的代码进行了一些改动



0 0
原创粉丝点击