服务端只需要返回如下格式的字符串,ExtJs就可以解析并自动生成分页数据。
{
totalProperty: //当前查询的记录总数
root:
[
{ id:, name: "sam" }, //每条记录
{ id:, name: "sam" }
]
}
准备:
@TableNames VARCHAR(2000) , --表名,可以是多个表,但不能用别名
@PrimaryKey VARCHAR(100) , --主键,可以为空,但@Order为空时该值不能为空
@Fields VARCHAR(4000) , --要取出的字段,可以是多个表的字段,可以为空,为空表示select *
@PageSize INT , --每页记录数
@CurrentPage INT , --当前页,0表示第1页
@Filter VARCHAR(4000) = '' , --条件,可以为空,不用填 where
@Group VARCHAR(2000) = '' , --分组依据,可以为空,不用填 group by
@Order VARCHAR(2000) = '' , --排序,可以为空,为空默认按主键升序排列,不用填 order by
@RecordCount INT OUTPUT --总记录数,自己增加(总记录数)
AS
BEGIN
IF ( @PageSize > 100 )
BEGIN
SET @PageSize = 100;
END;
IF ( @PageSize < 5 )
BEGIN
SET @PageSize = 5;
END;
DECLARE @SortColumn VARCHAR(200);
DECLARE @Operator CHAR(2);
DECLARE @SortTable VARCHAR(200);
DECLARE @SortName VARCHAR(200);
IF @Fields = ''
SET @Fields = '*';
IF @Filter = ''
SET @Filter = 'Where 1=1';
ELSE
SET @Filter = 'Where ' + @Filter;
IF @Group <> ''
SET @Group = 'GROUP BY ' + @Group;
IF @Order <> ''
BEGIN
DECLARE @pos1 INT ,
@pos2 INT;
SET @Order = REPLACE(REPLACE(@Order, ' asc', ' ASC'), ' desc',
' DESC');
IF CHARINDEX(' DESC', @Order) > 0
IF CHARINDEX(' ASC', @Order) > 0
BEGIN
IF CHARINDEX(' DESC', @Order) < CHARINDEX(' ASC',
@Order)
SET @Operator = '<=';
ELSE
SET @Operator = '>=';
END;
ELSE
SET @Operator = '<=';
ELSE
SET @Operator = '>=';
SET @SortColumn = REPLACE(REPLACE(REPLACE(@Order, ' ASC', ''),
' DESC', ''), ' ', '');
SET @pos1 = CHARINDEX(',', @SortColumn);
IF @pos1 > 0
SET @SortColumn = SUBSTRING(@SortColumn, 1, @pos1 - 1);
SET @pos2 = CHARINDEX('.', @SortColumn);
IF @pos2 > 0
BEGIN
SET @SortTable = SUBSTRING(@SortColumn, 1, @pos2 - 1);
IF @pos1 > 0
SET @SortName = SUBSTRING(@SortColumn, @pos2 + 1,
@pos1 - @pos2 - 1);
ELSE
SET @SortName = SUBSTRING(@SortColumn, @pos2 + 1,
LEN(@SortColumn) - @pos2);
END;
ELSE
BEGIN
SET @SortTable = @TableNames;
SET @SortName = @SortColumn;
END;
END;
ELSE
BEGIN
SET @SortColumn = @PrimaryKey;
SET @SortTable = @TableNames;
SET @SortName = @SortColumn;
SET @Order = @SortColumn;
SET @Operator = '>=';
END;
DECLARE @type VARCHAR(50);
DECLARE @prec INT;
SELECT @type = t.name ,
@prec = c.prec
FROM sysobjects o
JOIN syscolumns c ON o.id = c.id
JOIN systypes t ON c.xusertype = t.xusertype
WHERE o.name = @SortTable
AND c.name = @SortName;
IF CHARINDEX('char', @type) > 0
SET @type = @type + '(' + CAST(@prec AS VARCHAR) + ')';
DECLARE @TopRows INT;
SET @TopRows = @PageSize * @CurrentPage + 1;
--print('
-- DECLARE @SortColumnBegin ' + @type + '
-- SET ROWCOUNT ' + cast(@TopRows as varchar(8)) + '
-- Select @SortColumnBegin=' + @SortColumn + ' FROM ' + @TableNames + ' ' + @Filter + ' ' + @Group + ' orDER BY ' + @Order + '
-- SET ROWCOUNT ' + cast(@PageSize as varchar(8)) + '
-- Select ' + @Fields + ' FROM ' + @TableNames + ' ' + @Filter + ' AND ' + @SortColumn + '' + @Operator + '@SortColumnBegin ' + @Group + ' orDER BY ' + @Order + '
--')
EXEC('
DECLARE @SortColumnBegin ' + @type + '
SET ROWCOUNT ' + @TopRows + '
Select @SortColumnBegin=' + @SortColumn + ' FROM ' + @TableNames + ' ' + @Filter + ' ' + @Group + ' orDER BY ' + @Order + '
SET ROWCOUNT ' + @PageSize + '
Select ' + @Fields + ' FROM ' + @TableNames + ' ' + @Filter + ' AND ' + @SortColumn + '' + @Operator + '@SortColumnBegin ' + @Group + ' orDER BY ' + @Order + '
');
IF @RecordCount IS NULL
BEGIN
DECLARE @sql NVARCHAR(4000);
SET @sql = N'SELECT @RecordCount=COUNT(*)' + N' FROM '
+ @TableNames + N' ' + @Filter;
EXEC sp_executesql @sql, N'@RecordCount int OUTPUT',
@RecordCount OUTPUT;
END;
END;
SQL Procedure
//测试
declare @recount int
EXEC usp_PagingLarge 'UserManagers inner join Departments on UserManagers.DepartmentId=Departments.DepartmentId inner join TbRoses on UserManagers.TbRoseId=TbRoses.TbRoseId ', 'UserManagers.UserId', '',10, 0, '', '', 'UserManagers.UserId asc',@recount
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.SqlClient;
using System.Data.Entity;
namespace App_code
{
public class Pager
{
/// <summary>
/// Entity分页。支持单表、多表
/// </summary>
/// <typeparam name="T">Entity实体类型,多表Join时可能应临时创建一个从主表实体类派生的新类并为其增加额外的属性,因为Join的其它表中的字段除了关系键会出现在主表实体的属性中,其它字段并不会出现,而通常你都需要查询出那些非关系键的字段</typeparam>
/// <param name="context">Entity上下文实例</param>
/// <param name="TableNames">单表时,表名,多表时,提供join子句,如:a inner join b on a.depatmentId=b.depatmentId inner join c on a.roleId=c.roleId</param>
/// <param name="PrimaryKey">主表主键,如a.Id</param>
/// <param name="Fields">期望查询出的字段,每个字段以逗号隔开或空字符,空字符则查询所有字段。指定字段如:a.userName,b.depatmentName,c.roleName……</param>
/// <param name="PageSize">每页显示几条记录?</param>
/// <param name="CurrentPage">页码</param>
/// <param name="Filter">where子句或空字符,带Where子句时不需要写where作前缀</param>
/// <param name="Group">group by子句或空字符,带group by子句时不需要写group by作前缀</param>
/// <param name="Order">order by子句或空字符,带order by子句时不需要写order by作前缀,如:a.Id desc,b.depatmentName asc</param>
/// <param name="RecordCount">总记录数</param>
/// <returns></returns>
public static List<T> GetPageList<T>( DbContext context , string TableNames , string PrimaryKey , string Fields , int PageSize , int CurrentPage , string Filter , string Group , string Order , ref int RecordCount)
{
SqlParameter [ ] parameters = {
new SqlParameter("@TableNames", TableNames),
new SqlParameter("@PrimaryKey", PrimaryKey),
new SqlParameter("@Fields", Fields),
new SqlParameter("@PageSize", PageSize),
new SqlParameter("@CurrentPage", CurrentPage),
new SqlParameter("@Filter", Filter),
new SqlParameter("@Group", Group),
new SqlParameter("@Order", Order),
new SqlParameter("@RecordCount", RecordCount)
};
parameters [ ].Direction = ParameterDirection.Output;
var records = context.Database.SqlQuery<T> ( "exec usp_PagingLarge @TableNames,@PrimaryKey,@Fields,@PageSize,@CurrentPage,@Filter,@Group,@Order,@RecordCount output" , parameters );
var List = records.ToList ( );
RecordCount = ( int ) parameters [ ].Value;
// 返回记录总数
return List;
}
}
}
Pager
GridPanel分页
客户端ExtJs
//数据模型,将查询用户、权限和部门三张表
Ext.define("UserManagerModel", {
extend: "Ext.data.Model",
fields: [
{ name: "UserId", type: "int" },
{ name: "RoseName", type: "string" }, //引用了权限表
{ name: "DepartmentName", type: "string" }, //引用了部门表
{ name: "LoginName", type: "string" },
{ name: "UserName", type: "string" },
{ name: "Mobile", type: "string" },
{ name: "Email", type: "string" },
{ name: "Photo", type: "string" },
{ name: "Address", type: "string" },
{ name: "Birthday", type: "string" },
{ name: "Sex", type: "string" },
{ name: "Sate", type: "string" }
]
}); //分页数据存储器
Ext.create("Ext.data.Store", {
storeId: "UserManagerDataStore",
model: "UserManagerModel",
autoLoad:true,
pageSize: ,//每页显示10条记录
proxy: {
type: "ajax",
url: "/Ashx/RecordsHandler.ashx",
extraParams: { params: { start: , limit: } },
reader: {
type: "json",
totalProperty: 'totalProperty', //此为默认,表示记录总数
idProperty: 'UserId', //数据模型的主键
root:"root"
}
}
}); var columns = [
{ xtype: "rownumberer" },//第一列显示行号
{ header: "登录名", dataIndex: "LoginName" },
{ header: "真实姓名", dataIndex: "UserName" },
{
header: "头像", dataIndex: "Photo", renderer: function (v) {
return "<img style='width:30%;height:30%;' src='" + v + "' />"
}
},
{ header: "角色", dataIndex: "RoseName" },
{ header: "所属部门", dataIndex: "DepartmentName" },
{ header: "生日", dataIndex: "Birthday" },
{ header: "手机", dataIndex: "Mobile" },
{ header: "电邮", dataIndex: "Email" },
{ header: "住址", dataIndex: "Address" },
{
header: "性别", dataIndex: "Sex", renderer: function (v) {
if (v == "true") { return "男" }
return "女";
}
},
{
header: "在职状态", dataIndex: "Sate", renderer: function (v) {
if (v == "true") { return "在职" }
return "离职";
}
},
]; Ext.create("Ext.grid.Panel", {
title: "员工信息查询",
renderTo: "employeeMsg",
forceFit: true,
frame: false,
height:,
store: "UserManagerDataStore",
columns: columns,
//显示分页信息的工具栏
bbar: new Ext.PagingToolbar({
store: "UserManagerDataStore",
displayInfo: true,//是否显示分页的额外信息
displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',//displayInfo为true时,此属性才能生效
emptyMsg: "没有记录"
})
});
服务端
如果分页的Aajx请求的是ASP.NET MVC的Action方法,则需要注意:首次请求页面时应返回视图,视图里的ExtJs被成功加载到客户端后才会开启Ajax请求,所以首次加载视图时应返回一个view而不可能是分页数据,视图渲染完毕后store才会发起Ajax请求到另一个Action,这时,你才能request到分页参数。而下面使用的是ashx文件,视图已经呈现,所以不存在像在Action方法中无法获取分页参数的问题。
namespace WebErp.Ashx
{
// Entity不支持返回多表,此处需要定义一个UserManager的派生,以便可以将查询结果中的DepartmentName和RoseName装入UnkonwObj
public class UnkonwObj:UserManager
{
public string DepartmentName { get; set; }
public string RoseName { get; set; }
}
public class RecordsHandler : IHttpHandler
{
public void ProcessRequest( HttpContext context )
{
var Response = context.Response;
var Request = context.Request;
Response.ContentType = "text/plain";
System.Text.StringBuilder s = new System.Text.StringBuilder ( );
//当前页码上的第一条记录的索引
int start = int.Parse ( context.Request [ "start" ] );
//每页显示几条记录
int limit = int.Parse ( context.Request [ "limit" ] );
//计算页码
int pageIndex = start / limit ;
//分页后的总页数
int pageCount = ;
//获取表的分页记录
int recordCount = ;
//jion句子
string tablejoin = "UserManagers inner join Departments on UserManagers.DepartmentId=Departments.DepartmentId inner join TbRoses on UserManagers.TbRoseId=TbRoses.TbRoseId ";
using ( var DBContext = new ErpContext ( ) )
{
//分页
var List = Pager.GetPageList<UnkonwObj> ( DBContext , tablejoin , "UserManagers.UserId" , "" , limit , pageIndex , "" , "" , "UserManagers.UserId" , ref recordCount);
//将记录序列化为Json格式的字符表示且分页必须加入totalProperty属性供客户端ExtJs获取并作出计算
s.Append ( JsonConvert.SerializeObject ( List , Formatting.Indented ).Insert ( , "root:" ).Insert ( , "totalProperty:" + recordCount + "," ).Insert ( , "{" ) ).Append("}");
context.Response.Write ( s.ToString() );
}
} public bool IsReusable
{
get
{
return false;
}
}
}
}
下拉框分页
起始跟上面的grid分页完全一样,只需要在combobox里指定pageSize的值>0就OK。
客户端
//员工数据模型
Ext.define("UserManagerModel", {
extend: "Ext.data.Model",
fields: [
{ name: "UserId", type: "int" },
{ name: "UserName", type: "string" }
]
}); //员工数据存储器
Ext.create("Ext.data.Store", {
storeId: "UserManagerStore",
model: "UserManagerModel",
autoLoad: true,
pageSize: ,//每页显示5条记录
proxy: {
type: "ajax",
url: "/Ashx/ComboboxHandler.ashx",
extraParams: { params: { start: , limit: }, comboboxType: "userManagerModel" },
reader: {
type: "json",
totalProperty: 'totalProperty', //此为默认,表示记录总数
idProperty: 'UserId', //数据模型的主键
root: "root"
}
}
}); {
xtype: "combobox",
id: "UserId",
width:,
fieldLabel: "仓库管理员",
emptyText: "------选择仓库管理员------",
store: "UserManagerStore",
mode: "remote",
triggerAction: "all",
displayField: "UserName",
valueField: "UserId",
editable: false,
listConfig: {
loadingText: '查找中.',
emptyText: '没有符合的数据',
},
pageSize: //下拉列表框的分页大小,大于0则自动创建分页栏
}
服务端
System.Text.StringBuilder s = new System.Text.StringBuilder ( );
int start = int.Parse ( context.Request [ "start" ] );
int limit = int.Parse ( context.Request [ "limit" ] );
int pageIndex = start / limit;
int recordCount = ;
var List = Pager.GetPageList<UserManager> ( DBContext , "UserManagers" , "UserManagers.UserId" , "" , limit , pageIndex , "" , "" , "UserManagers.UserId" , ref recordCount );
s.Append ( JsonConvert.SerializeObject ( List , Formatting.Indented ).Insert ( , "root:" ).Insert ( , "totalProperty:" + recordCount + "," ).Insert ( , "{" ) ).Append ( "}" );
Response.Write ( s.ToString ( ) );
下拉框智能提示
根据用户输入的关键字进行查询,将查询的数据显示在下拉项中,当用户从下拉项选中一条记录后,触发combobox的select事件,动态为GridPanel所绑定的Store添加查询参数,动态改变表格的数据。
function CreateStoreBySearchKey(options) {
var StoreID = options.StoreID;
var comboID = options.comboID;
var valueField = options.valueField;
var displayField = options.displayField;
var start = options.start;
var limit = options.limit;
var pageSize = options.pageSize;
var url = options.url;
var idProperty = options.idProperty;
var root = options.root;
var highlightColor = options.highlightColor;
var combobox = Ext.getCmp(comboID);
Ext.define(StoreID + "Model", {
extend: "Ext.data.Model",
fields: [
{ name: valueField, type: "int" },
{ name: displayField, type: "string" }
]
});
return Ext.create("Ext.data.Store", {
storeId: StoreID,
autoLoad: false,
model: StoreID + "Model",
pageSize: pageSize,
proxy: {
type: "ajax",
url: url,
extraParams: { params: { start: start, limit: limit } },
reader: {
type: "json",
totalProperty: "totalProperty",
idProperty: idProperty,
root: root
}
},
listeners: {
load: function (me, records) {
//高亮searchKey
var val = Ext.getCmp(comboID);
Ext.each(records, function (record) {
record.set(val.displayField, record.get(val.displayField).replace(new RegExp(val.getRawValue(), "gm"), "<b style='background:" + highlightColor + "'>" + val.getRawValue() + "</b>"));
});
}
}
});
}
调用CreateStoreBySearchKey函数将自动创建存储器和模型,不需要手动创建,但需为自动创建的Store显示地指定一个名称即可,如下所示:
xtype: "panel",
width: ,
border:false,
layout: 'anchor',
items: [
{
xtype: 'combobox',
id: "StockTableKeySearchCombo",
store: CreateStoreBySearchKey({
StoreID: "StockTableKeySearchStore",
comboID: "StockTableKeySearchCombo",
displayField: "StockName",
valueField: "StockTableId",
url: "/Ashx/SearchByKey.ashx",
pageSize: ,
start: ,
limit: ,
idProperty: "StockTableId",
root: "root",
highlightColor: "yellow"
}),
displayField: 'StockName',
valueField: "StockTableId",
emptyText:"根据仓库名查询",
minChars: , //最小中文字符
typeAheadDelay: ,//等待用户输入文本的毫秒数
typeAhead: true,//配置了typeAheadDelay后可使用此项,表示用户输入的内容能完整匹配下拉列表中的某条记录时将自动把后半截内容追加到输入框
hideLabel: true,
hideTrigger: true,
enableKeyEvents :true,
anchor: '100%',
multiSelect: false,
queryDelay: ,
queryMode: 'remote',
listeners: {
//嵌入查询图标
afterrender: function () {
this.setFieldStyle("background: url(/Img/Ico/search.png) no-repeat 8px center;padding-left:26px;");
},
//没有输入项时折叠下拉框同时使GridPanel绑定的Store重载
keyup: function (field) {
if (field.getRawValue() == "") {
this.collapse(true);
Ext.getStore("StockTableStore").fireEvent("beforeload");
Ext.getStore("StockTableStore").reload();//重载
}
},
select: function (field, newVal, oldVal) {
var textVal = field.getRawValue();
var pureText = textVal.replace(/<[^>]+>/g, "");//去掉高亮的html标记
field.setValue(pureText);
Ext.getStore("StockTableStore").fireEvent("beforeload"); //用户输入了查询参数,手动触发GridPanel所绑定的Store的beforeload事件,在事件中添加了查询参数=当前用户选择的文本
Ext.getStore("StockTableStore").reload();//重载
}
},
queryParam: 'StockTableKeySearch', //查询参数变量名,默认为 query,服务端通过Request["query"]获取,此变量名可以自定义
listConfig: {
loadingText: '查找中.',
emptyText: '没有符合的数据'
},
pageSize: //下拉列表框的分页大小,大于0则自动创建分页栏
}
]
}
GridPanel.tbar
GridPanel的存储器
Ext.create("Ext.data.Store", {
storeId: "StockTableStore",
model: "StockTableModel",
autoLoad: true,
pageSize: ,//每页显示10条记录
proxy: {
type: "ajax",
url: "/Ashx/RecordsHandler.ashx",
extraParams: { params: { start: , limit: }, selectType: "StockTableModel" },
reader: {
type: "json",
totalProperty: 'totalProperty', //此为默认,表示记录总数
idProperty: 'StockTableId', //数据模型的主键
root: "root"
}
},
listeners: {
beforeload: function () {
var comboCmp = Ext.getCmp("StockTableKeySearchCombo");
var SearchKey = comboCmp.getRawValue(); //用户选中下拉选项中的某一项记录的文本
/*如果需要获取ID,用combobox.getValue()居然只能得到文本,可考虑用在combobox的存储器上findBy查找与当前选中项的文本相匹配的ID,如:
var stockNameSearchValue = comboCmp.getRawValue();
var mixcollections = comboCmp.getStore().queryBy(function (record, id) {
清除高亮的html后进行比较
return record.get("StockName").replace(/<[^>]+>/g, "") == SearchKey
});
if (mixcollections.getCount() != 0) {
mixcollections = mixcollections.getRange()[0];
SearchID = mixcollections.get("StockTableId"); 得到ID
}*/
//向Store追加查询参数
Ext.apply(this.proxy.extraParams, { SearchKey: SearchKey ? SearchKey : "", /*SearchID: SearchID ? SearchID : ""*/ });
}
}
});
// #endregion
combobox的查询分页:服务端根据用户输入的关键字进行查询,然后将数据显示在下拉框选项中:
string StockTableKeySearch= Request [ "StockTableKeySearch" ];//combobox的queryParam
GridPanel的查询分页:服务端根据用户选择的下拉框选项的文本进行查询:
string SearchKey = Request [ "SearchKey" ];
完整代码包含了其它非智能提示的功能,一并扔这了:
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>仓库管理</title>
<script type="text/javascript" src="/ExtJs/ext-all-dev.js"></script>
<script type="text/javascript" src="/ExtJs/ext-lang-zh_CN.js"></script>
@Html.Partial( "LoadCssAndJs" )
<script src="/ExtJs/ux/MultiSelect.js"></script>
<script src="/ExtJs/ux/ItemSelector.js"></script>
<link href="/ExtJs/ux/css/ItemSelector.css" rel="stylesheet" />
<script type="text/javascript" src="/Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="/Scripts/jquery.formOperating.js"></script>
<style>
#DrugsErpCompanyItemselector {border: 1px solid #B5B8C8; }
#DrugsErpCompanyItemselector .x-boundlist { /*background: #DFEAF2;*/ }
#DrugsErpCompanyItemselector * { border: none;}
.panelBoder * {border: none;}
.panelRedBoder { border: 1px solid red; }
/*按钮a标签*/
.x-btn { text-decoration: none !important; }
.x-grid-cell-inner-action-col { text-align: center !important; }
</style>
</head>
<body class="flat-blue">
<div class="app-container">
<div class="row content-container">
<!--顶部导航-->
@Html.Partial( "TopNavbar" )
<!--左侧功能菜单-->
@Html.Partial( "FearturesTreeMenu" )
<!-- 右侧内容 -->
<div class="container-fluid">
<div class="side-body padding-top">
<dov id="box"></dov><!--表单-->
<!--itemselectorBox-->
</div>
</div>
</div>
<!-- 注脚 -->
@ViewData[ "Test" ]
@Html.Partial( "Footerbar" )
</div>
</body>
</html>
<script>
new Ext.QuickTips.init();//初始化提示信息类
Ext.form.Field.prototype.msgTarget = "title";//显示验证提示信息时为title模式
// #region 构造适用于gridPanel、TreePanel的全选反选取消的右键菜单
//hostComponent:右键菜单的宿主(一个gridPanel或TreePanel)
//如果IsComplete为true,则返回一个实现全选反选取消的完整菜单
//否则只返回一个实现全选反选取消的菜单子项,在函数外部可调用Ext.menu.Menu的add方法将子项添加到右键菜单中
function createSelectContextMenu(hostComponent, event, IsComplete) {
event.preventDefault();
var selModel = hostComponent.getSelectionModel();
var records = hostComponent.getStore().getRange();
//创建菜单子项
var items = [
{
text: "全选",
handler: function () {
selModel.selectAll();
}
},
{
text: "反选",
handler: function () {
records.forEach(function (r) {
var isSelected = selModel.isSelected(r);
if (isSelected) {
selModel.deselect(r);
}
else {
selModel.select(r, true);
}
});
}
},
{
text: "全部取消",
handler: function () {
selModel.deselectAll();
}
}
];
//创建完整菜单
var contextmenu = Ext.create("Ext.menu.Menu", {
items: items,
listeners: {
deactivate: function (menuSelf) {
menuSelf.destroy(); //消失后销毁自身
}
}
});
return IsComplete ? contextmenu : items;
}
// #endregion
// #region GridPanel编辑状态下弹出多选框,读取默认值。打包
function CreateItemselectorEditWindow(options) {
var windowTitle = options.windowTitle;
var fieldsetTitle = options.fieldsetTitle;
var storeId = options.storeId;
var editRecord = options.editRecord;
var tableModelFieldText = options.tableModelFieldText;
var tableModelFieldKey = options.tableModelFieldKey;
var cellModelFieldText = options.cellModelFieldText;
var cellModelFieldKey = options.cellModelFieldKey;
//将当前要编辑的记录取出来存入oldRec
var oldRecs = [];
var Keys = editRecord.get(tableModelFieldKey);
var Texts = editRecord.get(tableModelFieldText);
for (var i = ; i < Keys.length; i++) {
var obj = {
[cellModelFieldKey]: Keys[i],
[cellModelFieldText]: Texts[i]
};
oldRecs.push(obj);
}
Ext.create("Ext.window.Window", {
id: "EditWin",
title: windowTitle,
autoShow: true,
forceFit: true,
width: ,
style: "border:none;padding:10px;",
border: false,
modal: true,
layout: "anchor",
items: [
//创建表单
Ext.create("Ext.form.Panel", {
id: "EditForm",
forceFit: true,
style: "border:none;padding:10px;",
border: false,
items: [
{
xtype: "fieldset",
title: fieldsetTitle,
columnWidth: .100,
style: "padding:10px;",
items: [
{
xtype: "panel",
layout: "fit",
cls: "panelBoder",
height: ,
items: [
{
xtype: 'itemselector',
anchor: '100%',
id: 'xItemSelector',
style: "margin-bottom:10px;",
imagePath: '/ExtJs/ux/css/images/',
store: storeId,
displayField: cellModelFieldText,
valueField: cellModelFieldKey,
allowBlank: false,
blankText: "必须选择至少一项",
msgTarget: "title",
fromTitle: '可选',
toTitle: '已选',
listeners: {
afterrender: function () {
var xItemSelector = Ext.getCmp("xItemSelector");
var store = Ext.getStore(storeId);
store.add(oldRecs);//将当前编辑的记录插入存储器
var newRecs = store.getNewRecords();//获取刚插入的记录
xItemSelector.bindStore(store); //刷新数据存储器
xItemSelector.moveRec(true, newRecs); //将参数指定的记录插入到右侧选择框
var selectedRecords = xItemSelector.toField.store.getRange();
if (!window.xItemSelectorOldVal) { window.xItemSelectorOldVal = [] };
window.xItemSelectorOldVal = selectedRecords;
if (!window.xItemSelectorDefaultVal) { window.xItemSelectorDefaultVal = [] };//存储当前编辑的记录,以便还原为默认选中项
window.xItemSelectorDefaultVal = newRecs;
}
}
}
],
bbar: [
{
xtype: "pagingtoolbar",
store: storeId,
displayInfo: true,
displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
emptyMsg: "没有记录",
listeners: {
beforechange: function () {
var xItemSelector = Ext.getCmp("xItemSelector");
var selectedRecords = xItemSelector.toField.store.getRange();
if (!window.xItemSelectorOldVal) { window.xItemSelectorOldVal = [] };
window.xItemSelectorOldVal = selectedRecords;
xItemSelector.fromField.store.removeAll();
}
}
},
{
xtype: "panel",
items: [
{
xtype: "button", text: "撤回所选", style: "background:red;border:none;margin:10px 0;", handler: function () {
var xItemSelector = Ext.getCmp("xItemSelector");
var oldVal = window.xItemSelectorOldVal;
if (oldVal) {
xItemSelector.getStore().add(oldVal);
window.xItemSelectorOldVal = null;
}
xItemSelector.clearValue();
}
},
{
xtype: "button",
text: "还原默认选中项",
style: "background:red;border:none;margin:10px 10px;",
handler: function () {
var xItemSelector = Ext.getCmp("xItemSelector");
xItemSelector.fromField.store.removeAll();
xItemSelector.bindStore(xItemSelector.getStore());
xItemSelector.getStore().add(window.xItemSelectorDefaultVal);
xItemSelector.moveRec(true, window.xItemSelectorDefaultVal);//还原默认选择项
}
}
],
}
]
}
]
}
],
buttons: [
{
text: "确定", handler: function () {
var xItemSelector = Ext.getCmp("xItemSelector");
var xItemSelectorVal = Ext.getCmp("xItemSelector").getValue();
if (xItemSelectorVal.length == ) {
xItemSelector.up("panel").addCls("panelRedBoder");
}
else {
xItemSelector.up("panel").removeCls("panelRedBoder");
Ext.Ajax.request({
method: "post",
url: "/StockTable/UpdateStockUser",
params: { xStockUserIDs: xItemSelectorVal.join(","), xStockID: editRecord.get("StockTableId") },
success: function (r) {
var m = Ext.decode(r.responseText);
Ext.create('Ext.window.MessageBox', {
style: "border:none;",
frame: false,
border: false
}).
show({
title: "提示",
msg: m.msg,
buttons: Ext.MessageBox.YES,
width: ,
icon: Ext.MessageBox.YES
});
Ext.getStore("StockTableStore").reload();
},
failure: function () { }
});
}
}
}
]
})
],
listeners: {
beforeclose: function () { //销毁所有组件和存储器
var store = Ext.getStore(storeId);
this.destroy();
Ext.StoreManager.remove(store);
if (window.xItemSelectorDefaultVal) { window.xItemSelectorDefaultVal = null };
},
close: function () { }
}
});
}
// #endregion
// #region 返回被污染过的脏数据集合,这包含了所有被选中的下拉框选项的value
//gridStoreID:gridPanel的数据存储器的storeId
//cArray是数组,displayField:下拉框的key,valueField:下拉框的value,gridText:当前下拉框在gird中的字段名,comboStoreID:下拉框数据存储器的storeId
function GetDityRec(gridStoreID, cArray) {
//获取gridPanel中的脏数据
var drityDatas = Ext.getStore(gridStoreID).getModifiedRecords();
var submitRecord = [];
var transcript;
//遍历每条记录
drityDatas.forEach(function (gridRecord) {
Ext.each(cArray, function (obj) {
//获取下拉框数据存储器
var comboStore = Ext.getStore(obj.comboStoreID);
var key = gridRecord.get(obj.gridDisplayField);
comboStore.findBy(function (comboRecord) {
if (comboRecord.get(obj.displayField) == key) {
if (cArray.indexOf(obj) == ) {
transcript = gridRecord.data;//首次应拷贝行的副本,此后则直接赋值
}
transcript[obj.gridDisplayValueField] = parseInt(comboRecord.get(obj.valueField));
}
});
});
submitRecord.push(transcript);
});
return submitRecord;
}
// #endregion
// #region 根据用户输入的文本智能联想加载符合的数据
function CreateStoreBySearchKey(options) {
var StoreID = options.StoreID;
var comboID = options.comboID;
var valueField = options.valueField;
var displayField = options.displayField;
var start = options.start;
var limit = options.limit;
var pageSize = options.pageSize;
var url = options.url;
var idProperty = options.idProperty;
var root = options.root;
var highlightColor = options.highlightColor;
var combobox = Ext.getCmp(comboID);
Ext.define(StoreID + "Model", {
extend: "Ext.data.Model",
fields: [
{ name: valueField, type: "int" },
{ name: displayField, type: "string" }
]
});
return Ext.create("Ext.data.Store", {
storeId: StoreID,
autoLoad: false,
model: StoreID + "Model",
pageSize: pageSize,
proxy: {
type: "ajax",
url: url,
extraParams: { params: { start: start, limit: limit } },
reader: {
type: "json",
totalProperty: "totalProperty",
idProperty: idProperty,
root: root
}
},
listeners: {
load: function (me, records) {
//高亮searchKey
var val = Ext.getCmp(comboID);
Ext.each(records, function (record) {
record.set(val.displayField, record.get(val.displayField).replace(new RegExp(val.getRawValue(), "gm"), "<b style='background:" + highlightColor + "'>" + val.getRawValue() + "</b>"));
});
}
}
});
}
// #endregion
Ext.onReady(function () {
// #region 公司数据模型
Ext.define("DrugsErpCompanyTableModel", {
extend: "Ext.data.Model",
fields: [
{ name: "DrugsErpCompanyTableId", type: "int" },
{ name: "CompanyName", type: "string" }
]
});
// #endregion
// #region 公司数据存储器
Ext.create("Ext.data.Store", {
storeId: "DrugsErpCompanyTableStore",
model: "DrugsErpCompanyTableModel",
autoLoad: true,
pageSize: ,//每页显示5条记录
proxy: {
type: "ajax",
url: "/Ashx/ComboboxHandler.ashx",
extraParams: { params: { start: , limit: }, comboboxType: "drugsErpCompanyTableModel" },
reader: {
type: "json",
totalProperty: 'totalProperty', //此为默认,表示记录总数
idProperty: 'DrugsErpCompanyTableId', //数据模型的主键
root: "root"
}
}
});
// #endregion
// #region 仓库数据模型
Ext.define("StockTableModel", {
extend: "Ext.data.Model",
fields: [
{ name: "StockTableId", type: "int" },
{ name: "StockName", type: "string" },
{ name: "DrugsErpCompanyTableId", type: "int" },
{ name: "CompanyName", type: "string" },
{ name: "StockAddress", type: "string" },
{ name: "UserIDs", type: "auto" },
{ name: "UserNames", type: "auto" }
]
});
// #endregion
// #region 仓库信息分页数据存储器
Ext.create("Ext.data.Store", {
storeId: "StockTableStore",
model: "StockTableModel",
autoLoad: true,
pageSize: ,//每页显示10条记录
proxy: {
type: "ajax",
url: "/Ashx/RecordsHandler.ashx",
extraParams: { params: { start: , limit: }, selectType: "StockTableModel" },
reader: {
type: "json",
totalProperty: 'totalProperty', //此为默认,表示记录总数
idProperty: 'StockTableId', //数据模型的主键
root: "root"
}
},
listeners: {
beforeload: function () {
var stockNameSearchKey = Ext.getCmp("StockTableKeySearchCombo").getValue();
var drugsErpCompanySearchKey = Ext.getCmp("DrugsErpCompanyTableKeySearchCombo").getValue();
var rec = Ext.getCmp("DrugsErpCompanyTableKeySearchCombo").getStore().queryBy(function (record, id) {
return record.get("CompanyName").replace(/<[^>]+>/g, "") == drugsErpCompanySearchKey
});
if (rec.getCount() != ) {
rec = rec.getRange()[];
drugsErpCompanySearchKey = rec.get("DrugsErpCompanyTableId");
}
Ext.apply(this.proxy.extraParams, { drugsErpCompanySearchKey: drugsErpCompanySearchKey ? drugsErpCompanySearchKey : "", stockNameSearchKey: stockNameSearchKey ? stockNameSearchKey : "" });
}
}
});
// #endregion
// #region 仓库信息列配置
var columns = [
{ xtype: "rownumberer" },
{
header: "仓库名称", dataIndex: "StockName",
editor: {
xtype: "textfield",
allowBlank: false,//不允许空值
selectOnFocus: true//点开编辑时自动选中值以便于用户删除。
}
},
{
header: "所属公司", dataIndex: "CompanyName",
editor: {
xtype: "combobox", id: "DrugsErpCompanyComboEdit", store: "DrugsErpCompanyTableStore", mode: "remote", triggerAction: "all", displayField: "CompanyName", valueField: "CompanyName", editable: false,
istConfig: {
loadingText: '查找中.',
emptyText: '没有符合的数据',
},
pageSize: //下拉列表框的分页大小,大于0则自动创建分页栏
}
},
{
header: "仓库地址", dataIndex: "StockAddress",
editor: {
xtype: "textfield",
allowBlank: false,//不允许空值
selectOnFocus: true//点开编辑时自动选中值以便于用户删除。
}
},
{
header: "仓库管理员", dataIndex: "UserNames",
editor: {}
},
//特殊列
{
xtype: 'actioncolumn',
text: '操作',
width:,
items: [
{
tooltip: "删除", icon: '/Img/Ico/del.png',
handler: function (view, rowIndex, colIndex, node, e, record, rowEl) {
Ext.create('Ext.window.MessageBox', {
style: "border:none;",
frame: false,
border: false
}).show({
title: "警告",
msg: "确定要删除吗?",
buttons: Ext.MessageBox.YESNO,
width: ,
icon: Ext.MessageBox.WARNING,
fn: function (m) {
if (m == "yes") {
Ext.Ajax.request({
method: "post",
url: "/StockTable/DelStockTable",
params: { ID: record.get("StockTableId"), delType: "Single" },
success: function (m) {
var m = Ext.decode(m.responseText);
Ext.Msg.alert("", m.msg);
Ext.getStore("StockTableStore").removeAt(rowIndex);
},
failure: function () {
Ext.Msg.alert("", "http错误");
}
});
}
}
});
}
}
]
}
];
// #endregion
// #region 仓库表格
Ext.create("Ext.grid.Panel", {
id: "StockTablePanel",
title: "仓库信息查询",
renderTo: "box",
forceFit: true,
frame: false,
height: ,
store: "StockTableStore",
columns: columns,
selModel: new Ext.selection.CheckboxModel({ checkOnly: false, ignoreRightMouseSelection: true }),
tbar: [
{
text: "批量删除", id: "batchDelBtn", style: "background:white;border:none;",
disabled: true, handler: function () {
Ext.create('Ext.window.MessageBox', {
style: "border:none;",
frame: false,
border: false
}).show({
title: "提示",
msg: "暂时不提供批量删除",
buttons: Ext.MessageBox.YES,
width: ,
icon: Ext.MessageBox.WARNING
});
}
},
{
text: "批量更新", id: "batchUpdateBtn", style: "background:white;border:none;",
handler: function () {
var c = [
{ displayField: "CompanyName", valueField: "DrugsErpCompanyTableId", gridDisplayField: "CompanyName", gridDisplayValueField:"DrugsErpCompanyTableId",comboStoreID: "DrugsErpCompanyTableStore" },
];
var dityRecords = GetDityRec("StockTableStore", c);
if (dityRecords.length == ) {
Ext.create('Ext.window.MessageBox', {
style: "border:none;",
frame: false,
border: false
}).show({
title: "提示",
msg: "未做任何改动",
buttons: Ext.MessageBox.YES,
width: ,
icon: Ext.MessageBox.WARNING
});
return;
}
Ext.Ajax.request({
method:"post",
url: "/StockTable/UpdateStockTable",
params: {xStockTable:Ext.encode(dityRecords)},
success: function (r) {
var m = Ext.decode(r.responseText);
Ext.create('Ext.window.MessageBox', {
style: "border:none;",
frame: false,
border: false
}). show({
title: "提示",
msg: m.msg,
buttons: Ext.MessageBox.YES,
width: ,
icon: Ext.MessageBox.YES
});
Ext.getStore("StockTableStore").reload();
},
failure: function () { }
});
}
},
{
xtype: "panel",
width: ,
border:false,
layout: 'anchor',
items: [
{
xtype: 'combobox',
id: "StockTableKeySearchCombo",
store: CreateStoreBySearchKey({
StoreID: "StockTableKeySearchStore",
comboID: "StockTableKeySearchCombo",
displayField: "StockName",
valueField: "StockTableId",
url: "/Ashx/SearchByKey.ashx",
pageSize: ,
start: ,
limit: ,
idProperty: "StockTableId",
root: "root",
highlightColor: "yellow"
}),
displayField: 'StockName',
valueField: "StockTableId",
emptyText:"根据仓库名查询",
minChars: , //最小中文字符
typeAheadDelay: ,//等待用户输入文本的毫秒数
typeAhead: true,//配置了typeAheadDelay后可使用此项,表示用户输入的内容能完整匹配下拉列表中的某条记录时将自动把后半截内容追加到输入框
hideLabel: true,
hideTrigger: true,
enableKeyEvents :true,
anchor: '100%',
multiSelect: false,
queryDelay: ,
queryMode: 'remote',
listeners: {
//嵌入查询图标
afterrender: function () {
this.setFieldStyle("background: url(/Img/Ico/search.png) no-repeat 8px center;padding-left:26px;");
},
//没有输入项时折叠下拉框同时使GridPanel绑定的Store重载
keyup: function (field) {
if (field.getRawValue() == "") {
this.collapse(true);
Ext.getStore("StockTableStore").fireEvent("beforeload");
Ext.getStore("StockTableStore").reload();//重载
}
},
select: function (field, newVal, oldVal) {
var textVal = field.getRawValue();
var pureText = textVal.replace(/<[^>]+>/g, "");//去掉高亮的html标记
field.setValue(pureText);
Ext.getStore("StockTableStore").fireEvent("beforeload"); //用户输入了查询参数,手动触发GridPanel所绑定的Store的beforeload事件,在事件中添加了查询参数=当前用户选择的文本
Ext.getStore("StockTableStore").reload();//重载
}
},
queryParam: 'StockTableKeySearch', //查询参数变量名,默认为 query,服务端通过Request["query"]获取,此变量名可以自定义
listConfig: {
loadingText: '查找中.',
emptyText: '没有符合的数据'
},
pageSize: //下拉列表框的分页大小,大于0则自动创建分页栏
}
]
},
{
xtype: "panel",
width: ,
border: false,
layout: 'anchor',
items: [
{
xtype: 'combobox',
id: "DrugsErpCompanyTableKeySearchCombo",
store: CreateStoreBySearchKey({
StoreID: "DrugsErpCompanyTableKeySearchStore",
comboID: "DrugsErpCompanyTableKeySearchCombo",
displayField: "CompanyName",
valueField: "DrugsErpCompanyTableId",
url: "/Ashx/SearchByKey.ashx",
pageSize: ,
start: ,
limit: ,
idProperty: "DrugsErpCompanyTableId",
root: "root",
highlightColor: "yellow"
}),
displayField: 'CompanyName',
valueField: "DrugsErpCompanyTableId",
emptyText: "根据所属公司查询",
minChars: , //最小中文字符
typeAheadDelay: ,//等待用户输入文本的毫秒数
typeAhead: true,//配置了typeAheadDelay后可使用此项,表示用户输入的内容能完整匹配下拉列表中的某条记录时将自动把后半截内容追加到输入框
hideLabel: true,
hideTrigger: true,
enableKeyEvents: true,
anchor: '100%',
multiSelect: false,
queryDelay: ,
queryMode: 'remote',
listeners: {
//嵌入查询图标
afterrender: function () {
this.setFieldStyle("background: url(/Img/Ico/search.png) no-repeat 8px center;padding-left:26px;");
},
//没有输入项时折叠下拉框同时使GridPanel绑定的Store重载
keyup: function (field) {
if (field.getRawValue() == "") {
this.collapse(true);
Ext.getStore("StockTableStore").fireEvent("beforeload");
Ext.getStore("StockTableStore").reload();//重载
}
},
select: function (field, newVal, oldVal) {
var textVal = field.getRawValue();
var pureText = textVal.replace(/<[^>]+>/g, "");//去掉高亮的html标记
field.setValue(pureText);
Ext.getStore("StockTableStore").fireEvent("beforeload"); //用户输入了查询参数,手动触发GridPanel所绑定的Store的beforeload事件,在事件中添加了查询参数=当前用户选择的文本
Ext.getStore("StockTableStore").reload();//重载
}
},
queryParam: 'DrugsErpCompanyTableKeySearch', //查询参数变量名,默认为 query,服务端通过Request["query"]获取,此变量名可以自定义
listConfig: {
loadingText: '查找中.',
emptyText: '没有符合的数据'
},
pageSize: //下拉列表框的分页大小,大于0则自动创建分页栏
}
]
}
],
bbar:[
{
xtype:"pagingtoolbar",
store: "StockTableStore",
displayInfo: true,//是否显示分页的额外信息
displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',//displayInfo为true时,此属性才能生效
emptyMsg: "没有记录"
}
],
listeners: {
selectionchange: function (selectionModel, selectedRow){
if (selectionModel.selected.length > ) {
this.query("button[id='batchDelBtn']")[].enable();
return;
}
this.query("button[id='batchDelBtn']")[].disable();
},
itemcontextmenu: function (view, record, item, index, e) {
var menu = createSelectContextMenu(this, e, true);
menu.showAt(e.getXY());
}
},
plugins: [
{
ptype: "cellediting",
clicksToEdit: ,
listeners: {
//编辑某记录时在函数中创建弹出窗口同时定义记录所对应的模型和存储器
beforeedit: function (editor, context) {
if (context.colIdx == ) {
// #region 员工数据模型
Ext.define("UserManagerModel", {
extend: "Ext.data.Model",
fields: [
{ name: "UserId", type: "int" },
{ name: "UserName", type: "string" }
]
});
// #endregion
// #region 员工数据存储器
Ext.create("Ext.data.Store", {
storeId: "UserManagerStore",
model: "UserManagerModel",
autoLoad: true,
pageSize: ,//每页显示5条记录
proxy: {
type: "ajax",
url: "/Ashx/ComboboxHandler.ashx",
extraParams: { params: { start: , limit: }, comboboxType: "userManagerModel", editExcludeID: Ext.encode(context.record.get("UserIDs")) }, //排除当前列数据的ID
reader: {
type: "json",
totalProperty: 'totalProperty', //此为默认,表示记录总数
idProperty: 'UserId', //数据模型的主键
root: "root"
}
}
});
// #endregion
//创建多选框
CreateItemselectorEditWindow({
windowTitle: "正在编辑……",
fieldsetTitle:"选择仓库管理员",
storeId: "UserManagerStore",
tableModelFieldText: "UserNames",//GridPanel的Store所绑定的数据模型(DataModel)中的数组(auto)字段,显示在表格列上的多个文本所代表的字段名
tableModelFieldKey: "UserIDs",//GridPanel的Store所绑定的数据模型(Model)中的数组(auto)字段,显示在表格列上的多个文本所对应的键的字段名
cellModelFieldText: "UserName", //被编辑的列所对应的数据模型(DataModel)中的key
cellModelFieldKey: "UserId",//被编辑的列所对应的数据模型(DataModel)中的Value
editRecord: context.record
});
return false;
}
}
}
}
]
});
// #endregion
});
</script>
Full Code
分页带额外参数
extraParams: { params: { start: , limit: }, selectType : "All" }