ExtJS4.2 - 从 Hello World 到 自定义组件 - 01
经验、概述、项目搭建、国际化、HelloWorld、布局
—— 为爱女伊兰而奋斗
——少走弯路,简单才是王道
我接触ExtJS已有两年多时间,不过之前一直用ExtJS3,近几日因为工作需要,才开始使用ExtJS4,鉴于目前手头工作不多,故将一些感受写下,希望能对其他朋友有所帮助。
我在2011年前都没有系统的使用过WEB前端UI框架,其实那会儿,dwr、extjs、jquery-ui都是交替红火着,不过我是个懒惰而又保守的人,加之自己之前也用 html+css+jsavascript+el+tag 自己写过一些东西,也就没去碰这些东西,只是出于项目需要,零散的用过一些UI组件,如日历组件、树组件等。
2011年,作为一个项目的技术架构师,应客户的要求,前端必须采用ExtJS,于是我就硬着头皮上了。初次接触ExtJS的感觉,是完全颠覆了传统WEB前端开发方式,JSP页面上空空如也,内容都在js文件中,让人大脑发蒙。从 Hello World 一路写下去,布局、单表维护、树、选择型弹窗、复杂UI,写了两个星期,才算适应了这种开发方式,终于恍然大悟,明白应该如何使用ExtJS4进行WEB前端开发。
2013年初,本来想接触ExtJS4,然而,另一个项目要求前端必须采用JQuery Easy UI,然后就把ExtJS4丢到一边去了。在此我不得不赞赏JQuery Easy UI这个UI框架,可谓小身材大智慧,几十MB的体积,却包含了强大的功能。我只花了两天,就搭出了框架:分页搜索、查删增改、多选型下拉列表、树型下拉列表、表格型下拉列表、自定义验证、菜单、导航、布局、复杂UI;并且JQuery Easy UI的开发模式也比较吻合传统WEB前端开发方式,我对它非常认可,因此打算放弃ExtJS了。不过在此我还是必须指出,与Ext相比,JQuery Easy UI在稳定性,界面的专业化程度方面,还是稍逊一筹,不过,它依然是非常好的一个WEB前端UI组件框架,本人大力推荐!
几天前,由于一个项目的客户要求采用ExtJS4,于是我终于捡起这个东西,起初发现它的代码与ExtJS3比较,似乎改变很大,但是深入进去看看,其实架构还是在那里,只是很多类、组件被重构,但是代码更精简了一些。在呈现后的页面上审查元素,发现元素渲染方面也有了优化。比如一个表格的单元格,之前包了几层div,现在就是1个td加1个div。由于有了ExtJS3的基础,因此花了3天,我就从HelloWorld开始,一步步做到封装了一个查删增改的组件。
在此我想分享一下自己使用ExtJS做开发的一点经验:
1. 首先有心理准备,从传统WEB页面开发方式转为使用ExtJS方式,类似于从 C 开发转为 Java 开发,ExtJS方式是面向对象、组件化的,JSP文件只用于引入资源,页面上呈现的任何元素,都是ExtJS的组件,必须一个个创建,然后拼装组合。
一般而言,我们页面上的元素是这样组织的
视图
面板
组件
此外,一个弹出窗口的元素则是这样组织的
窗口
面板
组件
2. 此外,如果希望利用ExtJS的方便和美观,却又指望它速度快,那是不现实的。友情提示:谷歌浏览器解析ExtJS是最快的,最差的是IE浏览器。
3. 开发第一步,对ExtJS4做一个概览,也就是说,了解它是什么、适应于哪些场景、整体结构是怎样的,有了这个全局观念,开发中就心中有数。千万不要一开始就让自己纠结于细节中。
4. 然后应该开始着手写程序,从 Hello World 开始,不要嫌简陋,一步一步来,目的是大致了解使用ExtJS4开发的方式。不至于说起来头头是道,一动手就抓瞎。不过需要注意的是,ExtJS4的demo,多是在 Ext.onReady 中一路写下来,初学者看了,往往写了一个页面,到下一个页面,就不知道如何把这个页面的东西移过去。因此,我建议采用代码切割的方式,更容易从全局把握。
5. 布局、表格、表单、分页及查删增改、树型及表格型下拉列表、菜单、导航,这些都一一写过,就会有所感觉
6. 之后可以开始写自定义类、组件,扩展Ext的一些东西
7. 通过ExtJS4的API了解常用类、组件,了解其属性、方法、事件
可以看到,一直到7,才开始进入ExtJS4的细节了解,我再次强调,一定要从全局开始,逐步深入局部细节,并且,作为开发人员,绝大部分细节不是你需要了解的,开发中需要的、常用的,才需要认真研究一番。
1.下载ext-4.2.1-gpl
http://cdn.sencha.com/ext/gpl/ext-4.2.1-gpl.zip
2.下载extjs4帮助文档
3.下载 org.json.jar
主要是配置:URIEncoding , 防止get访问时中文参数出错
1.Eclipse - 新建动态WEB项目(test)
2.将 org.json.jar 加入 WEB-INF/lib 下
3.将 ext-4.2.1-gpl 引入项目
4.书写过滤器处理字符集、国际区域;书写监听器处理共享属性
5.书写servlet处理查删增改
6.书写all_zh_CN.js文件处理国际化公共资源
7.书写imp.jsp文件处理前端共享资源
package test.common;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodingFilter implements Filter {
private String encoding;// 编码,默认:utf-8
public EncodingFilter() {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 编码
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
try {
((HttpServletResponse) response).setContentType("text/html;charset=" + encoding);
} catch (Exception ignore) {
}
// 国际区域
String strLocale = null;
// 取会话中的语言版本
strLocale = (String)((HttpServletRequest)request).getSession().getAttribute("strLocale");
// 如果会话中没有指定,则取浏览器的语言版本
if (strLocale == null) {
strLocale = request.getLocale().toString();
}
request.setAttribute("strLocale", strLocale);
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
encoding = fConfig.getInitParameter("encoding");
encoding = encoding==null?"UTF-8":encoding.trim();
encoding = "".equals(encoding)?"UTF-8":encoding;
}
}
package test.common;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ApplicationListener implements ServletContextListener {
public ApplicationListener() {
}
public void contextInitialized(ServletContextEvent arg0) {
ServletContext application = arg0.getServletContext();
application.setAttribute("webRoot", application.getContextPath());// WEB应用根目录
application.setAttribute("pageSize", 20);// 分页尺寸
}
public void contextDestroyed(ServletContextEvent arg0) {
}
}
package test.action;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class TestAction extends HttpServlet {
private static final long serialVersionUID = 1L;
private static JSONArray datas;
public TestAction() {
super();
iniDatas(); // 初始化数据
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
myProcess(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
myProcess(request, response);
}
// 处理入口
private void myProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 处理类型
String myProcessType = request.getParameter("myProcessType");
myProcessType = myProcessType==null?"0":myProcessType.trim();
myProcessType = "".equals(myProcessType)?"0":myProcessType;
if("0".equals(myProcessType)) {
myProcess_pageQuery(request, response); // 分页搜索
} else if("1".equals(myProcessType)) {
myProcess_save(request, response); // 添加或更新
} else if("2".equals(myProcessType)) {
myProcess_delete(request, response); // 删除
} else if("9".equals(myProcessType)) {
myProcess_submit(request, response); // 其他 - 测试表单提交
}
}
/** 分页搜索
*
* 输出 JSON对象,结构
* {
* total: 111,// 总行数
* root: // 数据
* [
* {
* id: "记录ID;自增型",
* name: "姓名",
* sex: "性别;F|M",
* tel: "电话",
* addr: "地址",
* email: "Email"
* },
* ...
* ]
* }
*/
private void myProcess_pageQuery(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 重写 Ext Store 的 beforload 事件传递的自定义参数
String name = request.getParameter("name");
String tel = request.getParameter("tel");
// Ext Store Proxy 自动传递的分页参数
int start = 1;
int limit = 0;
try {
start = Integer.parseInt(request.getParameter("start"));
limit = Integer.parseInt(request.getParameter("limit"));
} catch (Exception ignore) {
}
JSONObject joAll = getDatas(name, tel, start, limit);
PrintWriter out = response.getWriter();
out.println(joAll);
}
/** 添加或更新
*
* 输出 JSON对象,结构
* {
* success: true|false,// 是否成功
* msg: "消息"
* }
*/
private void myProcess_save(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
String sex = request.getParameter("sex");
String name = request.getParameter("name");
String tel = request.getParameter("tel");
String addr = request.getParameter("addr");
String email = request.getParameter("email");
id = id==null?"":id.trim();
PrintWriter out = response.getWriter();
JSONObject jo = new JSONObject();
String result = null;
if("".equals(id)) {
result = add(name, sex, tel, addr, email);
} else {
result = upd(id, name, sex, tel, addr, email);
}
try {
if("OK".equals(result)) {
jo.put("success", true);
} else {
jo.put("success", false);
jo.put("msg", result);
}
} catch (JSONException ignore) {
}
out.println(jo.toString());
}
/** 删除
*
* 输出 JSON对象,结构
* {
* success: true|false,// 是否成功
* msg: "消息"
* }
*/
private void myProcess_delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String ids = request.getParameter("ids");
PrintWriter out = response.getWriter();
JSONObject jo = new JSONObject();
String result = del(ids);
try {
if("OK".equals(result)) {
jo.put("success", true);
} else {
jo.put("success", false);
jo.put("msg", result);
}
} catch (JSONException ignore) {
}
out.println(jo.toString());
}
/** 其他 - 测试表单提交
*
* 输出 JSON对象,结构
* {
* success: true|false,// 是否成功
* msg: "消息"
* }
*/
private void myProcess_submit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map
StringBuffer sb = new StringBuffer();
Set
Object[] vals = null;
for(Entry
sb.append(et.getKey()).append(": ").append(joinAry((Object[])et.getValue())).append("
");
}
PrintWriter out = response.getWriter();
JSONObject jo = new JSONObject();
try {
jo.put("success", true);
jo.put("msg", sb.toString());
} catch (JSONException ignore) {
}
out.println(jo.toString());
}
// 添加;返回 OK 表示成功,否则为失败消息
private String add(String name, String sex, String tel, String addr, String email) {
name = name==null?"":name.trim();
tel = tel==null?"":tel.trim();
sex = sex==null?"":sex.trim();
sex = "F".equals(sex)?"F":"M";
addr = addr==null?"":addr.trim();
email = email==null?"":email.trim();
if("".equals(name) || name.length()>20 ||
"".equals(tel) || !tel.matches("^\\d{10,12}$") ||
!"".equals(email) && !email.matches("\\w+@\\w+[.][a-z]+") ||
addr.length()>50 ) {
return "非法操作";
}
int len = datas.length();
JSONObject jo = new JSONObject();
try {
jo.put("id", len+1);
jo.put("name", name);
jo.put("sex", sex);
jo.put("tel", tel);
jo.put("addr", addr);
jo.put("email", email);
} catch (JSONException ignore) {
}
datas.put(jo);
return "OK";
}
// 更新;返回 OK 表示成功,否则为失败消息
private String upd(String id, String name, String sex, String tel, String addr, String email) {
name = name==null?"":name.trim();
tel = tel==null?"":tel.trim();
sex = sex==null?"":sex.trim();
sex = "F".equals(sex)?"F":"M";
addr = addr==null?"":addr.trim();
email = email==null?"":email.trim();
if("".equals(name) || name.length()>20 ||
"".equals(tel) || !tel.matches("^\\d{10,12}$") ||
!"".equals(email) && !email.matches("\\w+@\\w+[.][a-z]+") ||
addr.length()>50 ) {
return "非法操作";
}
JSONObject jo = null;
int len = datas.length();
String id2 = null;
boolean flag = false;
for (int i = 0; i < len; i++) {
try {
jo = datas.getJSONObject(i);
id2 = jo.isNull("id")?"":jo.getString("id");
id2 = id2==null?"":id2.trim();
if(id2.equals("") || id2.equals("D") || !id2.equals(id)) {
continue;
}
jo.put("name", name);
jo.put("sex", sex);
jo.put("tel", tel);
jo.put("addr", addr);
jo.put("email", email);
flag = true;
break;
} catch (JSONException ignore) {
continue;
}
}
return flag?"OK":"没有该数据{"+id+"}";
}
// 删除;返回 OK 表示成功,否则为失败消息
private String del(String ids) {
if("".equals(ids)) {
return "非法操作";
}
ids = ","+ids+",";
JSONObject jo = null;
int len = datas.length();
String id2 = null;
boolean flag = false;
for (int i = 0; i < len; i++) {
try {
jo = datas.getJSONObject(i);
id2 = jo.isNull("id")?"":jo.getString("id");
id2 = id2==null?"":id2.trim();
if(id2.equals("") || id2.equals("D") || ids.indexOf(","+id2+",")<0) {
continue;
}
jo.put("id", "D");// 标志删除
flag = true;
} catch (JSONException ignore) {
continue;
}
}
return flag?"OK":"没有这些数据{"+ids+"}";
}
/* 分页搜索
*
* @param name姓名
* @param tel电话
* @param start起始索引号
* @param limit结束索引号
*
* @return JSON对象,结构
* {
* total: 111,// 总行数
* root: // 数据
* [
* {
* id: "记录ID;自增型",
* name: "姓名",
* sex: "性别;F|M",
* tel: "电话",
* addr: "地址",
* email: "Email"
* },
* ...
* ]
* }
*/
private JSONObject getDatas(String name, String tel, int start, int limit) {
name = name==null?"":name.trim();
int len = datas.length();
JSONObject jo = null;
String name2 = null;
String tel2 = null;
JSONArray ja = new JSONArray();
String id2 = null;
for(int i=0;i try { jo = datas.getJSONObject(i); id2 = jo.isNull("id")?"":jo.getString("id"); if(id2.equals("") || id2.equals("D")) { continue; } if(!"".equals(name)) { name2 = jo.isNull("name")?"":jo.getString("name"); name2 = name2==null?"":name2.trim(); if(name2.indexOf(name)<0) { continue; } } if(!"".equals(tel)) { tel2 = jo.isNull("tel")?"":jo.getString("tel"); tel2 = tel2==null?"":tel2.trim(); if(tel2.indexOf(tel)<0) { continue; } } ja.put(jo); } catch (JSONException ignore) { } } limit = limit<=0?20:limit; int total = ja.length(); start = start>=total?total-1:(start<0?0:start); int end = start+limit-1; end = end>=total?total:(end JSONArray ja2 = new JSONArray(); for(int i=0;i if(i>=start && i<=end) { try { ja2.put(ja.getJSONObject(i)); } catch (JSONException ignore) { } } } JSONObject joAll = new JSONObject(); try { joAll.put("total", total); joAll.put("root", ja2); } catch (JSONException ignore) { } return joAll; } /* 初始化数据,数据结构 * [ * { * id: "记录ID;自增型", * name: "姓名", * sex: "性别;F|M", * tel: "电话", * addr: "地址", * email: "Email" * }, * ... * ] */ private void iniDatas() { if(datas==null) { datas = new JSONArray(); JSONObject jo = null; for(int i=0;i<3000;i++) { jo = new JSONObject(); try { jo.put("id", i+1); jo.put("name", "无名氏"+(i+1)); jo.put("sex", i%4==0?"F":"M"); jo.put("tel", "152"+(int)((99999999-11111111+1)*Math.random()+11111111)); jo.put("addr", "无名小镇"+(int)((9999-1111+1)*Math.random()+1111)+"号"); jo.put("email", "wuming"+(int)((999999-111111+1)*Math.random()+111111)+"@wuming.com"); } catch (JSONException ignore) { } datas.put(jo); } } } // 辅助方法:数组的 join private String joinAry(Object[] ary) { int len = ary==null?0:ary.length; if(len==0) { return ""; } StringBuffer sb = new StringBuffer(); for(Object o:ary) { sb.append(o).append(", "); } int ix = sb.lastIndexOf(","); if(ix>=0) { sb.deleteCharAt(ix); } return sb.toString(); } }
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
var i18n_all = { title: { dataList: "数据表格", addOpe: "添加操作", editOpe: "编辑操作", delOpe: "删除操作", guide: "第 {0} 步,共 {1} 步" }, lable: { }, btn: { pre: "上一步", next: "下一步", add: "添加", edit: "编辑", del: "删除", save: "保存", reset: "重置" }, msg: { addConfirm: "是否确认添加", addOK: "添加成功", addErr: "添加失败", editConfirm: "是否确认更新", editOK: "编辑成功", editErr: "编辑失败", delConfirm: "是否确认删除", delOK: "删除成功", delErr: "删除失败" } }; <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%-- EXT-CSS文件 --%> <link rel="stylesheet" type="text/css" href="${webRoot }/res/extjs-4.2.0/resources/css/ext-all.css" /> <%-- EXT-JS引导文件(根据模式决定调入 ext_all.js 或 ext_all_dev.js) --%> <script type="text/javascript" src="${webRoot }/res/extjs-4.2.0/bootstrap.js">script> <%-- EXT-国际化文件,strLocale 在过滤器中已设置 --%> <script type="text/javascript" src="${webRoot }/res/extjs-4.2.0/locale/ext-lang-${strLocale }.js">script> <script> // 定义3个变量,用于 .js 文件 var global_webRoot = "${webRoot}";// WEB应用根目录 - 在监听器中已设置 var global_pageSize = "${pageSize}";// 分页大小 - 在监听器中已设置 var global_strLocale = "${strLocale}";// 国际区域 - 在过滤器中已设置 script> <script type="text/javascript" src="${webRoot }/locale/common/all_${strLocale }.j">script> var i18n_test = { title: { test1: "伊兰.ExtJS4专区" }, lable: { }, btn: { }, msg: { test1: "你好!欢迎光临伊兰.ExtJS4专区!" } }; var i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件 //Ext.application({// 程序入口;作用类似 Ext.onReady ;用于 MVC 架构 //name : "HelloExt", //launch : function() { //var p = getPanel(); // //Ext.create("Ext.container.Viewport", { //layout : "fit", //items : [ //p //] //}); //} //}); // 入口 Ext.onReady(function(){ // 程序入口,页面加载完后会自动调用 Ext.onReady // 获取面板 var p = getPanel(); // 通过视图呈现面板 // Viewport 为顶层容器,以页面 body 元素作为载体,呈现内容 Ext.create("Ext.container.Viewport", { layout : "fit", // fit 布局:填满容器 items : [ // Viewport包含的内容 p ] }); }); // 创建并返回面板 function getPanel() { //var p = new Ext.panel.Panel({// 普通加载 //// ... //}); var p = Ext.create("Ext.panel.Panel", {// 动态加载 title : i18n_my.title.test1, html : i18n_my.msg.test1 // 如果这个 Panel 不是通过 Viewport 呈现,则可通过 renderTo 指定呈现载体 //,renderTo: Ext.getBody()// 以页面 body 元素作为呈现载体 //,renderTo: "d01"// 以ID为 d01 的元素作为呈现载体 }); // 返回面板 return p; } <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ include file="/common/imp.jsp"%><%-- 共享文件 --%> <%-- 本页面国际化JS文件 --%> <script src="${webRoot }/locale/test/test_${strLocale }.js">script> <%-- 本页面功能JS文件 --%> <script src="${webRoot }/test01/test.js">script> <%-- --%> 启动服务器,浏览:http://localhost:8080/test/test01/test.jsp 这2个方法将在Ext加载完毕后自动调用 其中,Ext.application是ExtJS4引入的新方式,用于ExtJS4的MVC架构 顶层容器,视图组件,可以包含其他容器类组件 以 body 元素作为呈现载体 主要配置为: layouot: “布局”, items: [ 成员,通常为面板 ] 面板,容器类组件,可以包含其他组件,包括容器类组件 如果不放在视图内,必需用 renderTo 指定呈现载体 主要配置为: title: “标题”, width: 500, // 宽度 height: 300, // 高度 items: [], // 成员 tbar: [], // 工具栏 bbar: [], // 状态栏 buttons: [], // 按钮 listeners: { // 事件监听器 activate: function(panel, eopt) { // 面板激活时的处理代码,panel表示面板自身 }, show: function(panel, eopt) { // 面板呈现时的处理代码,panel表示面板自身 }, .. } 创建Ext对象 区别犹如java中的 new *** 与 Class.forName(“***”).newInstance() 也就是说,后者是动态创建,这是ExtJS4的亮点,实现了按需加载 Viewport也好,Panel也好,在默认情况下,它们只能有1个成员,如果有多个成员,只会呈现第一个;那么,如何使它们能拥有和呈现多个成员呢?那么,这就要靠布局来实现。布局,就是用来组织多个成员的。 默认的布局方式,一般是 fit ,表示成员填充整个容器 如果容器需要划分为几个部分,如上中下,或左右等,凡涉及到上下左右中的,都使用边界布局,这也是最常用的布局 边界布局的名称是 border ,它将容器划分为 上下左右中 五大块,其中,只有 中 部是必需的 容器内的成员通过 region 属性,指示将自身装载到容器的哪个部分 上、下:分别为 north 、 south;这两个部分必须且只能规定高度 左、右:分别为 west、 east;这两个部分必须且只能规定宽度 中:为 center;高度和宽度都为自动,自动填充容器剩余的部分 var i18n_test = { ..., msg: { ..., test2_0_1: "这里是 north", test2_0_2: "这里是 west", test2_0_3: "这里是 center (伊兰.ExtJS4专区.边界布局)", test2_0_4: "这里是 east", test2_0_5: "这里是 south" } }; var i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件 // 入口 Ext.onReady(function(){ var p1 = getTop(); var p2 = getLeft(); var p3 = getCenter(); var p4 = getRight(); var p5 = getBottom(); Ext.create("Ext.container.Viewport", { layout : "border", // 边界布局 items : [ p1, p2, p3, // 只有中部是必需的;中部的大小是自动的,不可指定的 p4, p5 ] }); }); // 头部 function getTop() { var p = Ext.create("Ext.panel.Panel", { html : i18n_my.msg.test2_0_1, height: 80, // 底部必须且只能指定高度 border: false, region: "north" // 指示将自身装载到容器的头部 }); return p; } // 左边 function getLeft() { var p = Ext.create("Ext.panel.Panel", { html : i18n_my.msg.test2_0_2, width: 200, // 左边必须且只能指定宽度 collapsible: true, // 面板标题栏将出现一个 >> 图标,点击将折叠或展开面板 split: true, // 该面板的宽度将可用鼠标调整 region: "west" // 指示将自身装载到容器的左部 }); return p; } // 中部 function getCenter() { var p = Ext.create("Ext.panel.Panel", { html : i18n_my.msg.test2_0_3, bodyStyle: "border-width:1px 0px 1px 0px", region: "center" // 指示将自身装载到容器的中部 }); return p; } // 右边 function getRight() { var p = Ext.create("Ext.panel.Panel", { html : i18n_my.msg.test2_0_4, width: 120, // 右边必须且只能指定宽度 collapsible: true, // 面板标题栏将出现一个 >> 图标,点击将折叠或展开面板 region: "east" // 指示将自身装载到容器的右部 }); return p; } // 底部 function getBottom() { var p = Ext.create("Ext.panel.Panel", { html : i18n_my.msg.test2_0_5, height: 40, // 底部必须且只能指定高度 border: false, region: "south" // 指示将自身装载到容器的底部 }); return p; } <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%-- 共享文件 --%> <%@ include file="/common/imp.jsp"%> <%-- 本页面国际化JS文件 --%> <script src="${webRoot }/locale/test/test_${strLocale }.js">script> <%-- 本页面功能JS文件 --%> <script src="${webRoot }/test02/test.js">script> 浏览:http://localhost:8080/test/test02/test.jsp 注意左右面板的 << 和 >> 按钮;注意左面板与中部的分割线 我们常见页面右边是导航栏,手风琴布局可以实现 手风琴布局的名称是 accordion 每个成员面板都支持展开和折叠。 在任何时间里,只有一个成员面板处于展开状态,其他都被折叠。 注意:只有Ext.Panels和所有Ext.panel.Panel子项才可以使用accordion布局 var i18n_test = { ..., msg: { ..., // 届时将使用 Ext.String.format 方法填充 {0} 参数 test2_1_0: "这里是 {0} 的导航栏", test2_1_1: "系统管理模块", test2_1_2: "权限管理模块", test2_1_3: "基础数据管理模块", test2_1_4: "任务调度管理模块", test2_1_5: "数据同步管理模块" } }; var i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件 // 入口 Ext.onReady(function(){ var p1 = getLeft(); var p2 = getCenter(); Ext.create("Ext.container.Viewport", { layout : "border", items: [ p1, p2 ] }); }); // 左边 - 整体 function getLeft() { // 获取5个成员面板 var p1 = getLeft01(); var p2 = getLeft02(); var p3 = getLeft03(); var p4 = getLeft04(); var p5 = getLeft05(); // 创建并返回手风琴布局面板 var p = Ext.create("Ext.panel.Panel", { layout: "accordion", // 手风琴布局 title: i18n_my.title.test2_1, width: 200, collapsible: true, split: true, region: "west", items: [ p1, p2, p3, p4, p5 ] }); return p; } // 左边 - 第 1 个成员 function getLeft01() { var tt = i18n_my.msg.test2_1_1; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 2 个成员 function getLeft02() { var tt = i18n_my.msg.test2_1_2; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 3 个成员 function getLeft03() { var tt = i18n_my.msg.test2_1_3; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 4 个成员 function getLeft04() { var tt = i18n_my.msg.test2_1_4; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 5 个成员 function getLeft05() { var tt = i18n_my.msg.test2_1_5; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 中部 function getCenter() { var p = Ext.create("Ext.panel.Panel", { html : i18n_my.msg.test2_0_3, bodyStyle: "border-width:1px 0px 1px 0px", region: "center" }); return p; } <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%-- 共享文件 --%> <%@ include file="/common/imp.jsp"%> <%-- 本页面国际化JS文件 --%> <script src="${webRoot }/locale/test/test_${strLocale }.js">script> <%-- 本页面功能JS文件 --%> <script src="${webRoot }/test02/test1.js">script> 浏览:http://localhost:8080/test/test02/test1.jsp 点击左边每个子面板,可见其他子面板被这地,被点击的面板则展开 我们常见向导界面,卡片布局可以实现 卡片布局的名称是 card 在任何时间里,只有一个成员面板处于激活状态,其他都被隐藏。 需要自行开发导航按钮,以导航到不同的成员面板,实现 上一步、下一步 的功能 注意:由于card布局需要配合导航按钮,而Viewport不具备buttons配置,故不宜直接将Viewport配置为card布局,而应用一个Panel作为card布局的总容器,再将这个Panel加入到Viewport var i18n_test = { ..., msg: { ..., test2_2_02: "现在进行第 {0} 步操作..." } }; var i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件 var g_ix = 0; // 索引,决定card布局中,哪个成员面板被显示 // 入口 Ext.onReady(function(){ var p1 = getLeft(); var p2 = getCenter(); Ext.create("Ext.container.Viewport", { layout : "border", items: [ p1, p2 ] }); }); // 左边 - 整体 function getLeft() { // 获取5个成员面板 var p1 = getLeft01(); var p2 = getLeft02(); var p3 = getLeft03(); var p4 = getLeft04(); var p5 = getLeft05(); // 创建并返回手风琴布局面板 var p = Ext.create("Ext.panel.Panel", { layout: "accordion", // 手风琴布局 title: i18n_my.title.test2_1, width: 200, collapsible: true, split: true, region: "west", items: [ p1, p2, p3, p4, p5 ] }); return p; } // 左边 - 第 1 个成员 function getLeft01() { var tt = i18n_my.msg.test2_1_1; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 2 个成员 function getLeft02() { var tt = i18n_my.msg.test2_1_2; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 3 个成员 function getLeft03() { var tt = i18n_my.msg.test2_1_3; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 4 个成员 function getLeft04() { var tt = i18n_my.msg.test2_1_4; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 左边 - 第 5 个成员 function getLeft05() { var tt = i18n_my.msg.test2_1_5; var p = Ext.create("Ext.panel.Panel", { title: tt, html : Ext.String.format(i18n_my.msg.test2_1_0, tt), border: false }); return p; } // 中部 - 整体 function getCenter() { // 按钮:上一步 var btnPre = Ext.create("Ext.button.Button", { id: "btnPre", text: i18n_all.btn.pre, disabled: true, handler: function() { g_ix--; showPanel(); } }); // 按钮:下一步 var btnNext = Ext.create("Ext.button.Button", { id: "btnNext", text: i18n_all.btn.next, handler: function() { g_ix++; showPanel(); } }); // 获取4个成员面板 var p1 = getCenter01(); var p2 = getCenter02(); var p3 = getCenter03(); var p4 = getCenter04(); // 创建并返回卡片式布局面板 var p = Ext.create("Ext.panel.Panel", { id: "center1", layout: "card", title: i18n_my.title.test2_2, region: "center", items: [ p1, p2, p3, p4 ], buttons: [ btnPre, btnNext ] }); return p; } // 中部 - 第 1 个成员 function getCenter01() { var tt = 1; var p = Ext.create("Ext.panel.Panel", { title: Ext.String.format(i18n_all.title.guide, tt, "4"), html : Ext.String.format(i18n_my.msg.test2_2, tt), border: false }); return p; } // 中部 - 第 2 个成员 function getCenter02() { var tt = 2; var p = Ext.create("Ext.panel.Panel", { title: Ext.String.format(i18n_all.title.guide, tt, "4"), html : Ext.String.format(i18n_my.msg.test2_2, tt), border: false }); return p; } // 中部 - 第 3 个成员 function getCenter03() { var tt = 3; var p = Ext.create("Ext.panel.Panel", { title: Ext.String.format(i18n_all.title.guide, tt, "4"), html : Ext.String.format(i18n_my.msg.test2_2, tt), border: false }); return p; } // 中部 - 第 4 个成员 function getCenter04() { var tt = 4; var p = Ext.create("Ext.panel.Panel", { title: Ext.String.format(i18n_all.title.guide, tt, "4"), html : Ext.String.format(i18n_my.msg.test2_2, tt), border: false }); return p; } // 导航到某个面板 function showPanel() { // 处理索引号,激活索引号对应的面板 g_ix = g_ix<0?0:(g_ix>3?3:g_ix); Ext.getCmp("center1").getLayout().setActiveItem(g_ix); // 根据索引号,设置 上一步、下一步 按钮的可用性 Ext.getCmp("btnPre").setDisabled(g_ix<=0); Ext.getCmp("btnNext").setDisabled(g_ix>=3); } <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%-- 共享文件 --%> <%@ include file="/common/imp.jsp"%> <%-- 本页面国际化JS文件 --%> <script src="${webRoot }/locale/test/test_${strLocale }.js">script> <%-- 本页面功能JS文件 --%> <script src="${webRoot }/test02/test2.js">script> 浏览:http://localhost:8080/test/test02/test2.jsp 1.初始时,显示的是card布局中第1个成员面板, 上一步 按钮被禁用 2.点击 下一步 按钮后,显示第2个成员面板, 上一步、下一步按钮都被启用 3.到第4步时,由于这是最后一个面板,所以 下一步 按钮被禁用2.7. web.xml代码
2.8. all_zh_CN.js代码
2.9. imp.jsp代码
3. Hello World
3.1. 开发
3.1.1. 创建 /locale/test_zh_CN.js文件
3.1.2. 创建 /test01/test.js文件
3.1.3. 创建 /test01/test.jsp文件
3.1.4. 浏览
3.2. 讲解
3.2.1. Ext.application 和 Ext.ready
3.2.2. Ext.container.Viewport
3.2.3. Ext.panel.Panel
3.2.4. new ***({}) 与 Ext.create(“***”, {});
4. 布局
4.1. 边界(border)
4.1.1. 概述
4.1.2. 开发
4.1.2.1. 修改 /locale/test_zh_CN.js文件
4.1.2.2. 创建 /test02/test.js文件
4.1.2.3. 创建 /test02/test.jsp文件
4.1.2.4. 浏览
4.2. 手风琴(accordion)
4.2.1. 概述
4.2.2. 开发
4.2.2.1. 修改 /locale/test_zh_CN.js文件
4.2.2.2. 创建 /test02/test1.js文件
4.2.2.3. 创建 /test02/test1.jsp文件
4.2.2.4. 浏览
4.3. 卡片(card)
4.3.1. 概述
4.3.2. 开发
4.3.2.1. 修改 /locale/test_zh_CN.js文件
4.3.2.2. 创建 /test02/test2.js文件
4.3.2.3. 创建 /test02/test2.jsp文件
4.3.2.4. 浏览