在没接触Ext时都不知道javascript是一门可以面向对象的语言,接触之后喜欢上了javascript这门语言,看了一些js相关书籍,想起之前项目经理说过的一句话,只有站在创造者的角度去想问题,才能以最好的方法解决问题。如果我以Ext创造者的角度去考虑问题,那么我的技术可以更上一层楼吧!
因为没有用Ext做过真正项目,只是看了官方的例子和api文档,看了一些源码,以自己的理解山寨了一下,命名为Design。之前看了< javascript设计模式>,思路大部分都来源于这本书,做这个只是兴趣爱好,功能上有很多细节没有实现(工作量较大),项目中用到什么功能就去完善那个功能。之前失败五次都因设计上不合理不得不重新做,而这回感觉还不错所以就拿出来给客位看官瞧瞧,如有建议还请多多指教。
使用的类库:
jquery // 因为jquery的插件比较丰富所以放弃使用Ext Core而选择jquery
jquery 树插件 zTree
jscal2 日期控件
TrimPath javascript模板引擎
highcharts javascript 图表插件
下面先介绍主要的功能,下面列出的是项目中主要实现的功能。
1、类体系
Extjs中得类体系非常出色,这里我模仿了Extjs类体系中主要的功能,主要有 define(定义类)、create(创建对象)、widget(使用别名创建对象)。
define函数主要有2个参数,参数1是类名也是js文件的路径,参数2是配置参数。
Design.define(“Design.panel.Panel”, { extend: ‘Design. Compoment, // 继承某个类 requires: [‘Design. toolbar.Toolbar’], // 这个类需要关联其它的类 alias: 'widget.panel’, // 这个类的别名 mixins: ['Design.util.Observable'] // 混入某个类 constructure:function(){}, // 构造函数 });
在上面我们定义了一个类,就可以使用create函数来创建它。第一个参数是名称,第二个参数是参数,参数会被传送到上面的构造函数中。
var panel = Design.create("Design.panel.Panel",{ id: 'panel', name: '面板' });
widget与create几乎是一样的,唯一不同就是可以用别名来创建它,上面将Design.panel.Panel 别名定义成 panel。
var panel = Design.widget("panel",{ id: 'panel', name: '面板' });
2、Require按需加载JS文件,Extjs4中加入的功能,为了减少页面的压力,只加载需要的文件。
Design.setPath(“app”); //配置路径。 Design.require(“Design.panel.Panel”); //加载这个类,会加载 app/panel/Panel.js 这个文件。
上面两行代码是要去找 app/panel/Panel.js 并加载,在执行这个文件时 Design.define方法会检索extend、requires、mixins 属性会发现有关联类Design.Compoment、Design.toolbar.Toolbar’、 Design.util.Observable,所以会先加载这三个文件,完成后继续执行继承和混入。在Design.require方法执行完后会寻找页面中的Design.onRady方法并调用,在页面加载完成并且需要的文件加载完后执行,与jquery.ready类似。
3、Design.util.Observable 监听者类
继承或混入监听者对象可以使类可以被监听事件。
4、数据层
Design.data.Model 模型
Design.data.Store 数据源
Model可以定义字段,而Store可以看成是模型的集合,主要用于控件装载模型作为显示内容,与监听数据的变化以及时更新控件。
5、Design. Compoment 基础组件类
这个基础组件类实现了对象与对象之间相组合的规则(组合模式)。
以表格为例它就组合了很多个对象 row(行)、cell(列)、table(表格)、header(标题栏)以及panel(面板),每一个组件对象中都会有一个HTMLElement对象,组合后将会生成下面的html片段。
<div id="panel-1"> <div id = "header-1"> 标题 </div> <table id = "table-1"> <tr id = "row-1"> <td id = "cell-1"></td> </tr> </table> </div>其中id可以使用var panel = Design.getCmp('panel-1')来获取这个控件对象,panel. destroy ()来销毁这个控件。
还实现一些基础功能:显示、销毁、大小改变、初始化Element、初始化子项、回调函数 ….等功能,几乎所有组件都继承自Compoment类。
下面说说缺点,缺点是没能实现Extjs中得layout布局功能(没有思路)导致组件之间无法灵活的布局(但可以设置Style的float属性来布局)、浏览器兼容有时需要配置style属性自己来写兼容样式(也可以说灵活吧!),以及性能上有些不理想。先介绍到这里,下面是一些截图 与Extjs相比较,第一张图是 Extjs官方例子group-header-grid 第二张是Design做出的效果,也可以比较下Extjs与Design的代码。
Code:
Ext.require([ 'Ext.grid.*', 'Ext.data.*', 'Ext.util.*', 'Ext.state.*' ]); Ext.onReady(function() { Ext.QuickTips.init(); // sample static data for the store var myData = [] // 省略; /** * Custom function used for column renderer * @param {Object} val */ function change(val) { if (val > 0) { return '<span style="color:green;">' + val + '</span>'; } else if (val < 0) { return '<span style="color:red;">' + val + '</span>'; } return val; } /** * Custom function used for column renderer * @param {Object} val */ function pctChange(val) { if (val > 0) { return '<span style="color:green;">' + val + '%</span>'; } else if (val < 0) { return '<span style="color:red;">' + val + '%</span>'; } return val; } // create the data store var store = Ext.create('Ext.data.ArrayStore', { fields: [ {name: 'company'}, {name: 'price', type: 'float'}, {name: 'change', type: 'float'}, {name: 'pctChange', type: 'float'}, {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'} ], data: myData }); // create the Grid var grid = Ext.create('Ext.grid.Panel', { store: store, columnLines: true, columns: [{ text : 'Company', flex : 1, sortable : false, dataIndex: 'company' }, { text: 'Stock Price', columns: [{ text : 'Price', width : 75, sortable : true, renderer : 'usMoney', dataIndex: 'price' }, { text : 'Change', width : 75, sortable : true, renderer : change, dataIndex: 'change' }, { text : '% Change', width : 75, sortable : true, renderer : pctChange, dataIndex: 'pctChange' }] }, { text : 'Last Updated', width : 85, sortable : true, renderer : Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange' }], height: 350, width: 600, title: 'Grouped Header Grid', renderTo: 'grid-example', viewConfig: { stripeRows: true } }); });
Code
Design.Loader.setPath('Design', '../../src'); Design.require(['Design.grid.Panel', 'Design.data.Model','Design.data.Store']); // 本例中需要用到这三个类 var myData = [] // 数据较大省略 Design.onReady(function(){ Design.define('GridModel', { // 声明模型 extend: 'Design.data.Model', fields: [{ name: 'company', type: 'string' },{ name: 'price', type: 'string' },{ name: 'change', type: 'string' },{ name: 'pctChange', type: 'string' },{ name: 'lastChange', type: 'string' }] }); var store = Design.create('Design.data.Store', { // 创建模型集合 model: 'GridModel', data: myData }); function change(m,val) { if (val > 0) { return '<span style="color:green;">' + val + '</span>'; } else if (val < 0) { return '<span style="color:red;">' + val + '</span>'; } return val; } function pctChange(m,val) { if (val > 0) { return '<span style="color:green;">' + val + '%</span>'; } else if (val < 0) { return '<span style="color:red;">' + val + '%</span>'; } return val; } function lastChange(m,val) { return Design.dateFormat(new Date(val),"yyyy年MM月dd日"); } function price(m,val) { if (!val) { return val } return "$ " + val; } var grid = Design.widget('grid', { // 创建一个表格 title: { text: 'Grouped Header Grid' // 标题 }, style: { // 表格的样式 height: 350, // 表格高度 width: 600 // 表格宽度 }, columns: [{ // 列的配置与Extjs非常相似 text: 'company', width: 200, dataIndex: 'company', locked: true }, { text: 'Stock Price', columns: [ {text: 'price', width: 125, dataIndex: 'price', renderer: price}, {text: 'change', width: 125, dataIndex: 'change', renderer: change}, {text: 'pctChange', width: 125, dataIndex: 'pctChange', renderer: pctChange} ] }, { text: 'lastChange', width: 135, dataIndex: 'lastChange', renderer: lastChange }], drag: true, // 使表格变为可拖动相当定义成了Ext.window.Window store:store }); grid.show(); // 显示到页面 });
接下来发一些演示截图与代码,大部分都是模仿Extjs例子做的,感兴趣的话可以下载源码。
表格工具栏演示。
var grid = Design.widget('grid', { title: { text: 'toolbar' }, toolbar: [{ dock: 'top', items: [{ xtype: 'textfield', style: { 'float': 'left' }, labelWidth: 40, label: 'text', fieldStyle: { width: 100 }, handler: function() { } },{ xtype: 'radiogroup', style: { float: 'left' }, items: [ {boxLabel: 'radio1', value:'1', name: 'chartType', checked: true}, {boxLabel: 'radio2', name: 'chartType', value:'2'} ] },{ xtype:'combobox', label:'combobox', displayField: 'value', valueField: 'id', allowBlank: false, data: [{id: 1, value: 'javascript1'}, {id: 0, value: 'javascript2'}], name:'holdData' },{ xtype: 'button', iconCls: 'icon-plus', style: { 'float': 'left' }, label: 'button', handler: function() { } }] }], drag: true, bodyPadding: 0, columnHeight: 40, style: { height: 350, width: 600 }, columns: [ {text: 'company', width: 200, dataIndex: 'company', locked: true}, {text: 'price', width: 125, dataIndex: 'price', renderer: price}, {text: 'change', width: 125, dataIndex: 'change', renderer: change}, {text: 'pctChange', width: 125, dataIndex: 'pctChange', renderer: pctChange}, {text: 'lastChange', width: 135, dataIndex: 'lastChange', renderer: lastChange} ], store:store }); grid.show('body');
var grid = Design.widget('grid', { title: { text: 'grid' }, bodyPadding: 0, columnHeight: 40, style: { height: 350, width: 600 }, columns: [ {text: 'company', width: 200, dataIndex: 'company', locked: true}, {text: 'price', width: 125, dataIndex: 'price', locked: true, renderer: price}, {text: 'change', width: 125, dataIndex: 'change', renderer: change}, {text: 'pctChange', width: 125, dataIndex: 'pctChange', renderer: pctChange}, {text: 'lastChange', width: 135, dataIndex: 'lastChange', renderer: lastChange} ], store:store, drag: true, toolbar: [{ dock: 'bottom', items: [{ xtype: 'pagination', store: store, style: { float: 'right' } }] }] }); grid.show();
var form = Design.widget('form', { style:{ width: 400, height: 550 }, drag: true, title: { text: 'form' }, bodyPadding: 30, fieldDefaults: { labelWidth: 50, fieldStyle:{ width: 280 } }, items: [{ xtype:'textfield', id: 'name', label:'姓名', allowBlank: false, name:'name' },{ xtype:'combobox', label:'性别', displayField: 'value', valueField: 'id', allowBlank: false, data: [{id: 1, value: '男'}, {id: 0, value: '女'}], name:'holdData' },{ xtype:'datefield', id: 'date', label:'日期', allowBlank: false, name:'name' },{ xtype:'fieldcontainer', label:'电话', items:[ {xtype:'displayfield',value:'(',fieldStyle:{'padding-right':'5px'}}, {xtype:'textfield',labelWidth:0,fieldStyle:{width:'23px'}}, {xtype:'displayfield',value:')',fieldStyle:{'padding-left':'5px','padding-right':'5px'}}, {xtype:'textfield',labelWidth:0,fieldStyle:{width:'30px'}}, {xtype:'displayfield',value:'-',fieldStyle:{'padding-left':'5px','padding-right':'5px'}}, {xtype:'textfield',labelWidth:0,fieldStyle:{width:'30px'} }] },{ xtype:'textareafield', label:'备注', name:'name' },{ xtype:'fieldset', title:'选填内容', items:[{ xtype:'fieldcontainer', label:'爱好', labelWidth: 50, fieldStyle:{ width:185, height:100 }, items:(function(){ var items = [{ xtype:'radio', name:'color', fieldStyle:{ width:70 }, boxLabel:'美工' }] for(var i=0;i<10;i++){ items.push({ xtype:'radio', name:'color', fieldStyle:{ width:70 }, boxLabel:'码农' }) } return items })() },{ xtype:'textfield', label:'姓名', labelWidth: 50, fieldStyle:{ width:'185' } },{ xtype:'fieldcontainer', label:'宠物', items:[{ xtype:'checkbox', boxLabel:'dog' },{ xtype:'checkbox', boxLabel:'cat' }] }] }], buttons: [{ text: '确定', handler: function() { var name = Design.getCmp('name'); alert(name.getValue()); //alert('ff'); } }, { text: '取消', handler: function() { alert('cancel'); } }] }); form.show();
var form = Design.widget('form', { style:{ width: 605, height: 350 }, drag: true, title: { text: 'fieldcontainer' }, bodyPadding: 15, fieldDefaults: { labelWidth: 100, fieldStyle:{ width: 282 } }, items: [{ xtype:'textfield', label:'Email Address', allowBlank: false, name:'email', fieldStyle:{ width: 460 } },{ xtype:'fieldcontainer', label:'Date Range', items:[ {xtype:'datefield',name: 'startDate', labelWidth:0, allowBlank: false, fieldStyle:{width:'224px'}}, {xtype:'datefield',name: 'endDate', labelWidth:10, allowBlank: false, fieldStyle:{width:'224px'}} ] },{ xtype:'fieldset', title:'Details', items:[{ xtype:'fieldcontainer', label:'phone', labelWidth: 100, items:[ {xtype:'displayfield',value:'(',fieldStyle:{'padding-right':'5px'}}, {xtype:'textfield',labelWidth:0,fieldStyle:{width:'23px'}}, {xtype:'displayfield',value:')',fieldStyle:{'padding-left':'5px','padding-right':'5px'}}, {xtype:'textfield',labelWidth:0,fieldStyle:{width:'30px'}}, {xtype:'displayfield',value:'-',fieldStyle:{'padding-left':'5px','padding-right':'5px'}}, {xtype:'textfield',labelWidth:0,fieldStyle:{width:'30px'} }] },{ xtype:'fieldcontainer', label:'Time worked', labelWidth: 100, items:[ {xtype:'textfield',name: 'hours', labelWidth:0,fieldStyle:{width:'35px'}}, {xtype:'displayfield',value:'hours',fieldStyle:{'padding-right':'1px'}}, {xtype:'textfield',name: 'mins', fieldStyle:{width:'35px'}}, {xtype:'displayfield',value:'mins',fieldStyle:{'padding-right':'1px'}} ] },{ xtype:'fieldcontainer', label:'Full Name', labelWidth: 100, items:[ {xtype:'combobox',name: 'hours', labelWidth:0,fieldStyle:{width:'55px'},value: 'mrs', displayField: 'name', valueField: 'value', data: [{name: 'Mr', value: 'mr'},{name: 'Mrs', value: 'mrs'}, {name: 'Miss', value: 'miss'}]}, {xtype: 'textfield', name : 'firstName', labelWidth:10, allowBlank: false, fieldStyle:{width:'180px'}}, {xtype: 'textfield', name : 'lastName', labelWidth:10, allowBlank: false, fieldStyle:{width:'180px'}} ] }] }], buttons: [{ text: 'Save', handler: function() { alert('ff'); } }, { text: 'Cancel', handler: function() { alert('cancel'); } }] }); form.show();
var form = Design.widget('form', { style:{ width: 406, height: 460 }, drag: true, title: { text: 'fieldcontainer' }, bodyPadding: 15, fieldDefaults: { labelWidth: 100, fieldStyle:{ width: 180 }, labelStyle: { 'font-weight':'bold', 'padding':'0' }, labelAlign: 'top' }, items: [{ xtype:'fieldcontainer', label:'Your Name', labelWidth: 100, fieldDefaults: { labelWidth: 50, fieldStyle:{ width: 110 } }, items:[{ xtype: 'textfield', name: 'firstName', labelAlign: 'top', label: 'First', allowBlank: false }, { xtype: 'textfield', labelAlign: 'top', name: 'middleInitial', label: 'MI', labelWidth: 30, fieldStyle:{ width: 30 }, style:{ 'margin-left': '10px', 'margin-right': '10px' } }, { xtype: 'textfield', name: 'lastName', labelAlign: 'top', label: 'Last', allowBlank: false, fieldStyle:{ width: 200 } }] },{ xtype: 'textfield', name: 'firstName', vtype: 'email', label: 'Your Email Address', allowBlank: false, fieldStyle:{ width: 364 } }, { xtype: 'textfield', label: 'Subject', allowBlank: false, fieldStyle:{ width: 364 } }, { xtype: 'textareafield', label: 'Message', allowBlank: false, fieldStyle:{ width: 364, height: 130 } }], buttons: [{ text: 'Save', handler: function() { alert('ff'); } }, { text: 'Cancel', handler: function() { alert('cancel'); } }] }); form.show();
var grid = Design.widget('panel', { title: { text: 'grid' }, style: { height: 550, width: 900 }, bodyStyle: { overflow: 'hidden' }, drag: true, items: [{ xtype: 'grid', bodyPadding: 0, columnHeight: 40, onItemSelect: function(models, event) { //alert('') Design.getCmp('form').setModel(models[0]); }, stripeRows: true, style: { height: '100%', width: "60%", float: 'left' }, columns: [ {text: 'company', width: 200, dataIndex: 'company', locked: true}, {text: 'price', width: 125, dataIndex: 'price', renderer: price}, {text: 'change', width: 125, dataIndex: 'change', renderer: change}, {text: 'pctChange', width: 125, dataIndex: 'pctChange', renderer: pctChange}, {text: 'lastChange', width: 135, dataIndex: 'lastChange', renderer: lastChange}, {text: 'rating', width: 135, dataIndex: 'rating', renderer: rating} ], store:store },{ xtype: 'form', id: 'form', items: [{ xtype: 'fieldset', title: 'Company details', fieldDefaults: { labelWidth: '100' }, items: [{ xtype: 'textfield', label: 'Name', name: 'company' },{ xtype: 'textfield', label: 'Price', name: 'price' },{ xtype: 'textfield', label: '% Change', name: 'pctChange' },{ xtype: 'datefield', label: 'Last Updated', name: 'lastChange' }, { xtype: 'fieldcontainer', label: 'Rating', fieldDefaults: { name: 'rating', xtype: 'radio' }, items: [{ value: '0', boxLabel: 'A' }, { value: '1', boxLabel: 'B' }, { value: '2', boxLabel: 'C' }] }] }], style: { width: '40%', height: '100%', float: 'left' } }] }); grid.show('body');
var tree = Design.widget('tree',{ title: { text: 'tree' }, drag: true, style: { width: 300, height: 500, 'float': 'left' }, bodyStyle: { overflow: 'auto' }, datas: jsonData }); tree.show('body');
var tree = Design.widget('tabpanel',{ title: { text: 'tree' }, drag: true, style: { width: 600, height: 500 }, items: [{ xtype: 'panel', style: { width: '100%', height: '100%' }, html: '<h1>panel1</h1>', title: { text: 'panel1' } },{ xtype: 'panel', style: { width: '100%', height: '100%' }, html: '<h1>panel2</h1>', title: { text: 'panel2' } },{ xtype: 'panel', style: { width: '100%', height: '100%' }, html: '<h1>panel3</h1>', title: { text: 'panel3' } }] }); tree.show('body');
图表演示下面图表扩展自javascript highcharts 插件。
Design.Loader.setPath('Design', '../../src'); Design.require(['Design.chart.Chart']); var data = [['第一季度', 20],['第二季度', 20],['第三季度', 20],['第四季度', 20],['第五季度', 20]]; Design.onReady(function(){ var chart = Design.widget('chart', { title: { text: 'chart' }, drag: true, style: { width: 500, height: 500, 'float': 'left' } }); chart.show('body'); chart.setPieChart(data, '营业额'); });
代码下载:
http://download.csdn.net/detail/junjun16818/5108310