现在,创建视图,在Scripts\app\view目录下先创建目录Users,然后在该目录下创建View.js。要使用Grid显示用户信息,因而要从Grid面板派生出视图。定义的时候要注意视图的类名。还有就是一定要定义别名,因为在控制器中是使用widget方法创建的视图实例。如果不想定义别名,那就要修改创建实例的方式。具体的基本定义代码如下:
Ext.define('SimpleCMS.view.Users.View',{
extend: 'Ext.grid.Panel',
alias: 'widget.usersview',
title: "用户管理",
id: "usersView",
initComponent: function () {
var me = this;
me.callParent(arguments);
}
});
代码中的id可根据需要定义,在这里是不管有没有用,先定义。如果项目类太多,要注意避免id冲突。标题定义可有可无,因为是在标签页内,看自己需要。
Grid需要Store,因而先添加store配置项,使用的Store是Users,因而定义代码如下:
store: "Users",
列的定义,可以直接使用配置项定义,也可以在initComponent方法内定义,具体看情况,例如当前例子,要为列添加编辑组件,因而在initComponent方法内定义比较合适,代码如下:
me.columns= [
{ text: '用户名', dataIndex: 'Username', flex: 1 },
{ text: '电子邮件', dataIndex: 'Email', flex: 1 },
{ text: '角色', dataIndex: 'Roles', flex: 1 },
{ xtype: "datecolumn", text: '创建时间', dataIndex: 'Created', format:"Y-m-d H:i:s", width: 150 },
{ xtype: "datecolumn", text: '最后登录时间', dataIndex: 'Created', format:"Y-m-d H:i:s", width: 150 },
{ xtype: 'checkcolumn', dataIndex:"IsApproved", text: "允许登录", winth: 150 }
]
现在定义都是一些基础代码,还没定义编辑组件,这样有个好处,先调试好显示,再进入下一阶段,可以减少错误。在最后一个字段,使用了扩展CheckColumn,因而要在Ext包中将CheckColumn.js文件复制到Ext\ux目录下,并添加requires配置项,代码如下:
requires:["Ext.ux.CheckColumn"],
接着要在顶部工具栏添加一个分页工具条,代码如下:
me.tbar= {
xtype: "pagingtoolbar",
pageSize: 50, displayInfo: true, store: me.store
}
这里设置了每页显示的记录数为50条记录,可根据自己情况做调整。
最后要在底部工具栏添加一段说明文字,代码如下:
me.bbar = ["双击用户可进入编辑状态,用户密码默认为“123456”。重置密码可将用户密码重置为“123456”。"]
好了,视图的雏形已经出来了,可以调试一下效果了。在浏览器打开页面,并使用admin登录,切换到用户管理标签页将看到如图21所示的效果。
图21 没有设置好布局的用户管理视图
是不是觉得有点奇怪,底部工具栏居然在Grid的标题栏下。对于笔者来说,看到这情况就知道是布局没设置好造成Grid主体部分高度为0。对于新手,要调试原因也不难,尤其是现在有Illuminations插件,在Firebug中将面板切换到Illuminations面板,然后单击点选按钮,然后单击视图的标题栏,会在Firebug看到如图22所示的效果。
图22 在Illuminations面板找到对象
在元素右边的路径上,单击图中黄色高亮部分的视图,然后如图23那样把鼠标移动到视图对象上,将看到页面中,视图部分用红色边框圈起来了,这说明视图并没有填满整个标签页主体,说明标签页的布局出问题了。在VS中切换到主面板视图(MainPanel.js),在添加用户管理面板的地方加入以下代码:
layout:"fit"
图23 查看视图的位置
使用Fit布局后,视图就可填满标签页面板主体了,现在刷新一下页面,切换到用户管理标签页,就可看到如图24所示的正确的效果了。
图24 正确的视图显示
现在,在服务器端创建Users控制器,为Grid提供数据。在Controllers目录创建一个名称为UsersController的控制器。加入必要的引用后,将Index方法修改为List方法,返回结果为JObject,代码如下:
publicJObject List()
{
}
使用Membership提供者的GetAllUsers就可进行分页,唯一问题是不能进行排序,在这里就不做处理了。还有个要注意的地方就是角色返回的必须是数组形式,不然在为Combobox设置值的时候会出问题。
现在要返回的数据结构是这样的:
{
success : true or false,
total : 记录总数,
Msg : 错误信息,
data : 由当前页记录组成的数组
}
结构中,success表示返回结果是否成功。如果不成功,可通过Msg获取错误信息。如果成功,就从data中读取数据。结构中的total非常重要,客户端会根据该值来计算页数,所以一定要正确返回,不然分页就会乱。
在之前MyFunction类中创建WriteJObjectResult方法要适应该结构,就要加参数,不过重载该方法,也是不错的选择,代码如下:
publicstatic JObject WriteJObjectResult(bool success, int total,string message,JArray data)
{
return new JObject {
newJProperty("success",success),
new JProperty("total",total),
new JProperty("Msg",message),
new JProperty("data",data)
};
}
好了,现在可以完成List方法的代码了,代码如下:
publicJObject List()
{
try
{
int pagesize = 50;
int page = 0;
int.TryParse(Request["page"],out page);
if (page <= 0) page = 1;
int total = 0;
JArray ja = new JArray();
foreach (MembershipUser c inMembership.GetAllUsers(page - 1, pagesize, out total))
{
string[] rolesForUser =Roles.GetRolesForUser(c.UserName);
ja.Add(new JObject {
newJProperty("id",c.ProviderUserKey),
newJProperty("Username",c.UserName),
newJProperty("Email",c.Email),
newJProperty("IsApproved",c.IsApproved),
newJProperty("LastLoginDate",c.LastLoginDate.ToString("yyyy-MM-ddhh:mm:ss")),
newJProperty("Created",c.CreationDate.ToString("yyyy-MM-ddhh:mm:ss")),
newJProperty("Roles",new JArray(rolesForUser.Select(m=>m)))
});
}
returnMyFunction.WriteJObjectResult(true, total, "", ja);
}
catch (Exception e)
{
returnMyFunction.WriteJObjectResult(false, 0, e.Message, null);
}
}
上面代码,第一个要注意的是GetAllUsers方法中,分页居然是从0开始算第一页。第二个要注意的是使用GetRolesForUser方法根据用户名返回的角色是字符串数组,然后通过LINQ就可直接将它转换为JSON数组。其它代码没太大难度,不明白的可直接发邮件或留言给我。
为了防止意外情况,这里添加了try模块,在错误的时候会将错误信息作为Msg关键字的值返回。至于是否需要这样直接返回错误信息,这还是得根据项目或自己喜好去决定了。
而在客户端脚本,目前是没有处理错误信息的代码的,因而在这里返回的错误信息,客户端是看不到。如果要处理这样的错误信息,就要在Store的Proxy中监听exception事件。因为exception事件的回调函数是一致的,因而可以统一到一个函数中处理,就不用复制再复制了。
先切换到Index.cshtml,在Ext.ns下添加以下代码定义处理exception事件的回调:
SimpleCMS.ProxyException = function (proxy,response, opts) {
Ext.Msg.alert("错误信息",opts.error);
}
在exception事件的回调函数的第三个参数返回的是Operation对象,当success为false时,它会将Msg关键字的值复制到对象的error属性,因而直接调用该属性就可获得错误信息了。在这里要处理的意外情况其实还有很多,如服务器没有返回数据、响应延时等等,在这里就不一一列举了,大家可根据API的说明完善该函数。
接着,切换到Users的Store,在proxy定义内添加listeners配置项来监听exception事件,代码如下:
listeners:{
exception :SimpleCMS.ProxyException
}
或许会有人问,为什么不在控制器中为proxy写监听代码?这……笔者确实不知道怎么回答,还是老规矩,个人喜好吧。笔者认为,把什么东西都往控制器中塞,是否有这样的必要,代码是否真的这样就容易维护?这问题展开来估计又是骂战,还是根据个人喜好选吧!
要测试exception事件是否能正常执行,在pagesize的定义代码前添加以下代码抛出一个异常:
throw new Exception("发生错误了。");
重新生成一下解决方案,然后刷新页面,切换到用户管理将看到如图25所示的错误信息,说明监听代码运作正常。
图25 exception事件显示的错误信息
把抛出异常代码删除,然后再编译一下。在浏览器中关闭对话框,然后单击分页工具栏的刷新按钮,就可看到如图26所示的数据了。
用户数据列表已经完成了,余下就是完成添加、编辑、删除和重置密码操作了,这个,下文再说。
源代码下载:http://download.csdn.net/detail/tianxiaode/4600348