您的位置:首页 > 其它

后台管理框架之六 :View页面设计

2014-09-29 14:50 597 查看
  在前面技术选型章节中,已经提到了页面采用Razor(CShtml) + Jquery +EasyUI + js。在整个项目中,页面布局采用Html + EasyUI(个人喜欢避免使用JS代码生成EasyUI);而编辑类页面采用Razor(CShtml)
,其中验证框架采用MVC4 自带的jquery.validate.unobtrusive.js。

  JQuery
、Easyui 两个Js框架优点突出,我就不再一一描述了。这里再说说这段时间Razor给我的感觉:

  1.
Razor语法页面结构较Html更为简捷、清晰;

  2.
Razor语法对页面的操作做了很多封装,可以减少代码工作量;

  3.
Razor语法可充分利用后台代码,可以提供更为灵活、简捷的页面处理;

  4.
Razor语法提供了很多的Helper类,如@Html , @Model
,这些类与后台视图模型的显示、验证可以实现完全、紧密的绑定,一来使得减少较多的验证代码,二来可以提供前、后台数据验证的一致性。我认为这一点在“编辑类页面”是非常有用的,这也是我在页面模型中不使用jquery.validate.js验证框架和EasyUI validateBox,而是直接使用Mvc4自带的jquery.validate.unobtrusive.js验证框架的原因(虽然这个框架仍然是基于jquery.validate的)。
  一、页面部局

  整个项目前台页面布局采用EasyUI,这个基于JQuery
的开源JS框架非常好用,目前最新版本1.4,符合Html5规范,通过NuGet可发直接下载并加入到项目中。整个项目抓图如下:

  




  整个项目布局部分代码如下(可参照EasyUI官网布局示例源代码):

<body class="easyui-layout">
<div id="north" data-options="region:'north',split:false,border:false" style="height: 60px; overflow: hidden; background: #D2E0F2; line-height: 20px; color: #fff; font-family: Verdana, 微软雅黑,黑体;">
@Html.Partial( "_MainTopLayoutPartial" )
</div>
<div id="south" data-options="region:'south',split:false" style="height:30px; background: #D2E0F2; ">
@Html.Partial( "_MainBottomLayoutPartial" )
</div>
@*<div id="right" data-options="region:'east',split:true,collapse:true" title="East" style="width:200px;">
<ul class="easyui-tree" data-options="url:'./data/tree_data1.json',method:'get',animate:true,dnd:true"></ul>
</div>*@
<div id="left" data-options="region:'west',split:true" title="导航菜单" style="width:180px;">
@Html.Partial( "_MainLeftLayoutPartial" )
</div>
<div id="mainTitle" data-options="region:'center',title:'操作区',iconCls:'icon-ok',noheader:true">
@Html.Partial( "_MainCenterLayoutPartial" )
</div>
</body>

  二、视图模型

  视图模型是View的一部分,主要是对前后台数据交互的封装,如后台发往前台的编辑类数据、前台发往后台的查询结构数据等。使用视图模型的好处:

  1. 方便对前后台数据交互进行管理。Mvc可以根据传递的QueryParams参数值,自动构建参数模型,自动生成参数对象,不用再像Asp.net那样自行封装数据,减少很多工作量。

  2. 能对数据进行验证性规则定义,配合Mvc4验证框架和提供的一些Helper类,高效紧密结合,可以有效保证数据前后台一致性、有效性;

  3. 实现与实体数据模型的分离,避免实体数据模型直接暴露在前台。

这些视图模型类似于实体模型,主要是单一的数据承载类,其设计可以根据实际页面模型进行定义,无需过多关注。

  三、JS设计

  Js语言是一个强大的、面向对象的、弱类型的编程语言,弱类型导致她非常灵活,也非常容易出错,因为在编辑时解析器不检查语法,直到运行时才能发现问题。在View中,JS承担着View的前台页面逻辑处理,通常的处理方式是独立将JS代码存放在一个JS文件中,然后在页面中进行引用。这个项目也不可避免,总体来说,一切为了项目结构清晰、易读为主。

  在原来的项目中,我们的JS一般是针对性的进行编写,比如说,页面上有个Button,点击后应弹出一个对话框,一般的写法是:

  <input id=”btn011” type=”button”onclick=”btnClick” value=”点击” />

  ......  

  function btnClick(){

    alert(“btn011被点击”);

  }

  

  然后在一个页面Js中,可能有N个function定义,其实这也没有什么错。但是在EasyUI、ExtJs这类JS
_UI框架下可能会碰到“别扭”的地方:很多的页面基本上都是一个列表页面,再配合一个编辑页面,选中某条数据后再点击相关的按钮(如增、删、改、查)进行操作,基本很多操作类似,代码相似度极高,复制、粘贴大法将会产生大量的冗余代码,而且极有可能出现相同名称的函数。同名函数在单页面显示时没有影响,但是在UI
布局框架下,这些单页面都会作为同一个Document对象的内容进行加载,那么同名函数就会产生非常大的影响,函数被调用的永远是最后定义的那一个,很多时候会出现莫名其妙的所谓BUG。这是一个极其别扭的事,调试找错是极其费时费事的事情。

  一种解决办法是尽量使用匿名函数和匿名对象,但是匿名函数会导致代码可读性差、控制性差等问题,而且也会存在大量的冗余代码。所以在这个项目上,对冗余代码进行了一定程度的封装,另外针对列表页面和编辑页面做了抽象定义,个人感觉效果还不错,冗余减少,功能结构更清晰,以下是这部分的JS代码:

  

<span style="font-size:12px;">function options( tar, href, fn ) {
options.prototype.formTag = tar;
options.prototype.actionHref = href;
options.prototype.handler = fn;
};
/*
定义基本表格操作处理类
参数说明:
tableId : string , 当前表格对应的html Id
idField :  string, 当前表格数据的Id主键
参数说明:
两个参数必须提供,否则将抛出异常
*/
function baseGridHandler( tableId, field ) {
if ( ( tableId == undefined || tableId == "" ) )  // || ( field == undefined || field == "" )
throw "表格参数必须提供!"

var owner = this;
var selectRowValueTag = "SelectRowValue";
var DatagridValue = "DatagridValue";

if ( tableId.substr( 0, 1 ) != "#" )
tableId = "#" + tableId;
/*
表格对应的Html Table Id值
*/
var tableTagID = tableId;
var idField = field;
if ( idField == undefined )
idField = $( tableTagID ).datagrid( "options" ).idField;
if ( idField == null )
throw "表格必须提供IdField参数!";
/*
存储表格对应数据
数据格式:{ total : 0 , rows : [] }
默 认值:{ total : 0 , rows : [] }
*/
baseGridHandler.prototype.datas = {
total: 0, rows: []
};
/*
定义子编辑框尺寸
默 认值: { width: 550, height: 450 };
*/
baseGridHandler.prototype.borderSize = { width: 550, height: 450 };

baseGridHandler.prototype.toolbarOptions = {
showRefreshButton: true,
showAddButton: true,
showEditButton: true,
showDeleteButton: true,
showClearButton: true
};
/*
定义表格列表刷新操作配置内容
参数格式:{ actionHref : "",showInToolbar : true}
参数说明:
actionHref : List请求路径地址
showInToolbar : true
默 认值: null;
*/
baseGridHandler.prototype.listOptions = null;
/*
定义表格编辑操作配置内容
参数格式:{ actionHref : "" , actionViewHref : "" , formTag : "" , showInToolbar:true}
参数说明:
actionViewHref : Edit页面的请求路径地址
formTag : Edit编辑Dialog的form Id值
actionHref : Edit页面的请求路径地址
showInToolbar : true
默 认值: null;
*/
baseGridHandler.prototype.editOptions = null;
/*
定义表格删除操作配置内容
参数格式:{ actionHref : "" , showInToolbar : true }
参数说明:
actionView : 删除页面的请求路径地址
actionHref : Edit页面的请求路径地址
默 认值: null;
*/
baseGridHandler.prototype.deleteOptions = null;
/*
定义表格执行刷新操作之后应调用函数
参数格式:function(){}
默 认值: null;
*/
baseGridHandler.prototype.afterRefresh = null;
baseGridHandler.prototype.afterRowSelected = null;

showMessage = function ( msg ) {
$.messager.show( {
title: "提示",
msg: msg,
timeout: 3000
} );
}
/*
根据指定参数,执行表格数据刷新操作
参数格式:{Id : value},根据实体业务要求组合参数,如{Id : 1 , Name : 'san'}
默 认值:null
*/
baseGridHandler.prototype.doRefresh = function ( params ) {
$( tableTagID ).datagrid( "reload", params );
owner.onDataRowSelected( -1, null );
//if ( owner.listOptions != null ) {
//        $.ajaxRequest( {
//                url: owner.listOptions.actionHref,
//                data: params,
//                onSuccessHandler: function ( result ) {
//                        var data;
//                        if ( result.data == undefined )
//                                data = result;
//                        else
//                                data = resutl.data;
//                        owner.doFillData( data );
//                        if ( owner.afterRefresh != null )
//                                owner.call( afterRefresh, data );
//                }
//        } );
//}
};

/*
根据指定参数,执行表格数据编辑操作
参数格式:{Id : value},根据实体业务要求组合参数,如{Id : 1 , Name : 'san'}
默 认值:{Id : -1} 表格新增数据
*/
baseGridHandler.prototype.doEdit = function ( params ) {
if ( owner.editOptions != null ) {
if ( params == undefined )
params = { Id: -1 };
$.showDialog( {
width: owner.borderSize.width,
height: owner.borderSize.height,
href: owner.editOptions.actionViewHref,
params: params,
onClosedHandler: owner.doRefresh,
okHandler: owner.submitEditForm
} );
}
};

/*
根据指定参数,执行表格数据删除操作
参数格式:{Id : value},根据实体业务要求组合参数,如{Id : 1 , Name : 'san'}
*/
baseGridHandler.prototype.doDelete = function ( params ) {
if ( owner.deleteOptions == null )
throw "删除操作配置不能为空!";
if ( params == undefined )
throw "删除操作需要提供参数!";
var url = owner.deleteOptions.actionHref;
$.messager.confirm( '确认对话框', '确定删除选择的数据?', function ( r ) {
if ( r ) {
$.ajaxRequest( {
url: url,
data: param,
onSuccessHandler: owner.doRefresh
} );
};
} );
};

/*
使用数据参数刷新表格内容
参数格式:{total:0,row:[]}
*/
baseGridHandler.prototype.doFillData = function ( data ) {
owner.datas = data;
$( tableTagID ).datagrid( "loadData", data );
};

/*
使用指定数据参数设置表格属性
参数格式:{},具体格式参照Easyui datagrid options对象
*/
baseGridHandler.prototype.setGridOptions = function ( options ) {
if ( options == null || options == undefined ) return;
$( tableTagID ).datagrid( options );
}
/*
获取表格属性
参数格式:无
返回 值:datagrid Options对象
*/
baseGridHandler.prototype.getGridOptions = function () {
return $( tableTagID ).datagrid( "options" );
}
/*
使用指定数据参数设置表格属性
参数格式:value,可对存储的任何对象对象
返回 值:无
*/
baseGridHandler.prototype.setGridData = function ( value ) {
$( tableTagID ).data( DatagridValue, value );
}
/*
获取表格属性
参数格式:无
返回 值:任意对象
*/
baseGridHandler.prototype.getGridData = function () {
return $( tableTagID ).data( DatagridValue );
}

/*
根据指定条件执行搜索操作,搜索后自动填充当前表格
目前暂未实现
*/
baseGridHandler.prototype.doSearch = function () {

};

/*
执行当前表格数据导出操作
目前暂未实现
*/
baseGridHandler.prototype.doExport = function () {

};

/*
提交表格数据对应实体编辑框
此方法配置增加或修改的showDialog使用,在此dialog中的确定按钮点击时调用
参数格式:{cancle:true} ,如果将此参数值设置为true,可以取消Dialog对话框关闭
*/
baseGridHandler.prototype.submitEditForm = function ( e ) {
if ( owner.editOptions == null )
throw "编辑区配置不能为空!";
if ( $( owner.editOptions.formTag ).valid() ) {
$.ajaxRequest( {
url: owner.editOptions.actionHref,
data: $( owner.editOptions.formTag ).serialize()// 你的formid
} );
e.cancle = false;
} else e.cancle = true;
};

/*
获取当前表格选中行的主键值
*/
baseGridHandler.prototype.selectedValue = function () {
return $( tableTagID ).data( selectRowValueTag );
};

/*
获取当前表格选中行的数据
返回结果为选中的行对象,可以使用对象.属性获取相应数值,如var id = row.Id;
*/
baseGridHandler.prototype.selectedRow = function () {
return $( tableTagID ).datagrid( "getSelected" );
};

/*
当前表格选择行被修改时引发,单行选择时被触发
参数格式:rowIndex , rowData
参数说明 :
rowIndex : 所选择的行索引
rowData : 所选择的行数据
*/
baseGridHandler.prototype.onDataRowSelected = function ( rowIndex, rowData ) {
if ( 0 > rowIndex ) {
if ( owner.toolbarOptions.showEditButton == true ) {
$( '#' + edit_toolbar_button.id ).linkbutton( "disable" );
};
if ( owner.toolbarOptions.showDeleteButton == true ) {
$( '#' + delete_toolbar_button.id ).linkbutton( "disable" );
};
if ( owner.toolbarOptions.showClearButton == true ) {
$( '#' + clearSelected_toolbar_button.id ).linkbutton( "disable" );
};
$( tableTagID ).datagrid( "unselectAll" );
$( tableTagID ).removeData( selectRowValueTag );  //移除blah

return;
};
var code = $( tableTagID ).data( selectRowValueTag );
if ( code != rowData[idField] ) {
$( tableTagID ).data( selectRowValueTag, rowData[idField] );
if ( owner.toolbarOptions.showEditButton == true ) {
$( '#' + edit_toolbar_button.id ).linkbutton( "enable" );
};
if ( owner.toolbarOptions.showDeleteButton == true ) {
$( '#' + delete_toolbar_button.id ).linkbutton( "enable" );
};
if ( owner.toolbarOptions.showClearButton == true ) {
$( '#' + clearSelected_toolbar_button.id ).linkbutton( "enable" );
};
if ( owner.afterRowSelected != null )
owner.afterRowSelected( rowIndex, rowData );
}
};

/*
定义刷新按钮被点击执行的操作
直接会调用doRefresh方法
*/
baseGridHandler.prototype.onRefreshClicked = function () {
owner.doRefresh();
showMessage( "刷新获取数据完成!" );
};

baseGridHandler.prototype.onClearSelectedClicked = function () {
owner.onDataRowSelected( -1, null );
}
/*
定义默认新增操作
*/
baseGridHandler.prototype.onCreateClicked = function () {
owner.doEdit();
};
/*
定义默认编辑操作
*/
baseGridHandler.prototype.onEditClicked = function () {
var value = owner.selectedValue();
if ( value == undefined || value == null ) {
showMessage( "请选择对应数据后,再执行修改操作!" );
return;
}
var params = {};
params[idField] = value;

//owner.doEdit( appUtils.builderParamsToJson( [{ "name": idField, "value": value }] ) );
owner.doEdit( params );
};

/*
定义默认编辑操作
*/
baseGridHandler.prototype.onDeleteClicked = function () {
var value = owner.selectedValue();
if ( value == undefined || value == null ) {
showMessage( "请选择对应数据后,再执行删除操作!" );
return;
}

var params = {};
params[idField] = value;

owner.doDelete( params );
};

/*
定义传递给grid获取数据的loader对象
特别提示:默认loader直接返回true,不做任何处理,子类对象应重写当前方法
参数格式:param,successCallback,errorCallback
参数说明:
param:json对象
successCallback:数据加载成功时回调函数
errorCallback:数据加载失败时回调函数
参数格式:
param:参数对象传递给远程服务器。
successCallback:fn(data):当检索数据成功的时候会调用该回调函数。
errorCallback: fn()当检索数据失败的时候会调用该回调函数。
返回结果:
返回false可以放弃本次请求动作。
*/
baseGridHandler.prototype.dataLoader = function ( param, successCallback, errorCallback ) {
var options = $( tableTagID ).datagrid( "options" );
$.ajaxRequest( {
url: options.url,
data: param,
onSuccessHandler: successCallback,
onErrorHandler: errorCallback
} );

return true;
};

/*
定义表格工具栏
*/
var refresh_toolbar_button = null;
var create_toolbar_button = null;
var edit_toolbar_button = null;
var delete_toolbar_button = null;
var clearSelected_toolbar_button = null;

baseGridHandler.prototype.toolbar = [];
_builderToolbar = function () {
if ( owner.toolbarOptions.showRefreshButton == true ) {
refresh_toolbar_button = {
id: appUtils.randomId(),
text: '刷新',
iconCls: 'icon-reload',
handler: owner.onRefreshClicked
};
owner.toolbar.push( refresh_toolbar_button, '-' );
};
if ( owner.toolbarOptions.showAddButton == true ) {
create_toolbar_button = {
id: appUtils.randomId(),
text: '添加',
iconCls: 'icon-add',
handler: owner.onCreateClicked
};
owner.toolbar.push( create_toolbar_button );
};
if ( owner.toolbarOptions.showEditButton == true ) {
edit_toolbar_button = {
id: appUtils.randomId(),
text: '修改',
iconCls: 'icon-edit',
disabled: true,
handler: owner.onEditClicked
};
owner.toolbar.push( edit_toolbar_button );
};
if ( owner.toolbarOptions.showDeleteButton == true ) {
delete_toolbar_button = {
id: appUtils.randomId(),
text: '删除',
iconCls: 'icon-cut',
disabled: true,
handler: owner.onDeleteClicked
};
owner.toolbar.push( delete_toolbar_button, '-' );
};
if ( owner.toolbarOptions.showClearButton == true ) {
clearSelected_toolbar_button = {
id: appUtils.randomId(),
text: '清除选择',
iconCls: 'icon-remove',
disabled: true,
handler: owner.onClearSelectedClicked
};
owner.toolbar.push( clearSelected_toolbar_button, '-' );
};

$( tableTagID ).datagrid( {
toolbar: owner.toolbar
} );
};
_initDatagridProperty = function () {
$( tableTagID ).datagrid( {
onSelect: owner.onDataRowSelected,
loader: owner.dataLoader
} );
};
_doFillData = function ( result ) {
baseGridHandler.prototype.doFillData( resut.data );
};

/*
执行对象初始化
*/
baseGridHandler.prototype.init = function () {
_builderToolbar();
_initDatagridProperty();
};

};
</span>

  

  在页面具体操作时,可以直接定义一个baseGridHandler
的对象,然后将相关按照的操作指定到这个对象的相关方法,或者直接调用这个对象的init方法,自动为datagrid增加按钮;当然也可以在此对象里定义一些事件,如编辑完成后应执行的函数,然后由定义的对象去实现,就可以进行各项功能的扩展;还有一种办法是,直接重定这个对象的方法,改变整个窗体的处理逻辑也行。

  整个项目View处理没有什么特别的地方,基本上想说的也就这么多,下一步将介绍业务逻辑层的设计思路。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: