在Coolite Asp.Net MVC 中用Extjs 实现带Check的树

本文介绍如何使用ExtJS构建一个带Check状态的树形结构,并实现节点选中时其子节点同步选中,取消选中时父节点也会相应取消的功能。包括前后端代码示例,展示了完整的权限管理和角色权限保存逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  带Check状态的树,功能说明:

  1)当一个节点选中时,其子节点也都选中;

  2)当一个节点取消选中时,其子节点也都取消选中,其父接点也都需要取消选中

  效果见图:

  2010052413353478.jpg2010052413355746.jpg2010052413361781.jpg

图像上虚框是真正操作的CheckBox

废话不多说,上代码,首先看看例子的数据结构

2010052413450530.jpg

看到这几张图,也许你有自己做的思路了吧,先看看我做的吧

我看需要查询出权限,还得查询出角色有的权限。何不一起查呢。。

 

ContractedBlock.gifExpandedBlockStart.gif代码
public AjaxStoreResult GetRoleRights(string roleId)
{
var query
= (from right in DomainService.RightSet.OrderBy(a => a.Parent.Code).OrderBy(a => a.SerialNo)
select
new RoleRightState
{
Id
= right.Code,
RightName
= right.Name + (right.State == true ? "(必选)" : ""),
ParentId
= right.Parent == null ? "" : right.Parent.Code,
HasCheck
= false
}).ToList();
foreach (var rrs in query)
{
rrs.HasCheck
= Platform.RightContext.HasRight(rrs.Id, new Role { Id = roleId });
}
return new AjaxStoreResult(query, query.Count);
}

 

看到这里也许有人问怎么突然出现AjaxStoreResult是什么东西啊??其实这就是Json的一个封装。其实这个是Coolite的MVC demo有的,让你看看代码吧:)

 

ContractedBlock.gifExpandedBlockStart.gif代码
public class AjaxStoreResult : ActionResult
{
public AjaxStoreResult() { }

public AjaxStoreResult(object data)
{
this.Data = data;
}

public AjaxStoreResult(object data, int totalCount) : this(data)
{
this.TotalCount = totalCount;
}

public AjaxStoreResult(StoreResponseFormat responseFormat)
{
this.ResponseFormat = responseFormat;
}

private object data;
public object Data
{
get { return this.data; }
set { this.data = value; }
}

private int totalCount;
public int TotalCount
{
get { return this.totalCount; }
set { this.totalCount = value; }
}

private StoreResponseFormat responseFormat = StoreResponseFormat.Load;
public StoreResponseFormat ResponseFormat
{
get { return this.responseFormat; }
set { this.responseFormat = value; }
}

private SaveStoreResponse saveResponse;
public SaveStoreResponse SaveResponse
{
get
{
if(this.saveResponse == null)
{
this.saveResponse = new SaveStoreResponse();
}
return this.saveResponse;
}
}

public override void ExecuteResult(ControllerContext context)
{
switch (this.ResponseFormat)
{
case StoreResponseFormat.Load:
StoreResponseData storeResponse
= new StoreResponseData();
storeResponse.Data
= JSON.Serialize(this.Data);
storeResponse.TotalCount
= this.TotalCount;
storeResponse.Return();
break;
case StoreResponseFormat.Save:
Response response
= new Response(true);
response.Success
= this.SaveResponse.Success;
response.Msg
= this.SaveResponse.ErrorMessage;
StoreResponseData saveResponse
= new StoreResponseData();
saveResponse.Confirmation
= this.SaveResponse.ConfirmationList;
response.Data
= saveResponse.ToString();

response.Return();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}

public enum StoreResponseFormat
{
Load,
Save
}

public class SaveStoreResponse
{
private bool success = true;
private string errorMessage;

public bool Success
{
get { return this.success; }
set { this.success = value; }
}

public string ErrorMessage
{
get { return this.errorMessage; }
set { this.errorMessage = value; }
}

public ConfirmationList ConfirmationList { get; set; }
}

 

我们后方资源准备的半足了,好像缺少什么啊,对了存储角色权限啊。继续写代码:

 

ContractedBlock.gifExpandedBlockStart.gif代码
[CheckHasRight(RightToCheckFor = Rights.RoleManager)]
public AjaxStoreResult SaveRoleRights(string roleId)
{
AjaxStoreResult ajaxStoreResult
= new AjaxStoreResult(StoreResponseFormat.Save);
try
{
StoreDataHandler dataHandler
= new StoreDataHandler(HttpContext.Request["data"]);
ChangeRecords
<RoleRightState> data = dataHandler.ObjectData<RoleRightState>();
ConfirmationList confirmationList
= dataHandler.BuildConfirmationList("Id");
Role role
= DomainService.RoleSet.Where(a => a.Id == roleId).FirstOrDefault();
Platform.OperateLog.Warn(
"设置角色:" + role.Name + "的角色权限信息");
if (role != null)
{
var check
=
from item
in data.Updated
where item.HasCheck
select item;
var uncheck
=
from item
in data.Updated
where !item.HasCheck
select item;
List
<RoleRightState> checkeditem = new List<RoleRightState>(check);
foreach (var item in uncheck)
{
bool hasParentCancel=uncheck.Count(a=>a.Id==item.ParentId)>0;
//情况 是否下级权限取消.
var eqlevel = from r in DomainService.RightSet
where r.Parent.Code == item.ParentId && r.Code != item.Id
&& hasParentCancel
select
new RoleRightState
{
Id
= r.Code,
ParentId
= r.Parent.Code,
HasCheck
= true,

};
checkeditem.AddRange(eqlevel);
RoleRight old
= DomainService.RoleRightSet.FirstOrDefault(a => a.Role.Id == role.Id && a.Right.Code == item.Id);
if (old != null)
DomainService.DeleteObject(old);
}

//foreach (RoleRight old in DomainService.RoleRightSet.Where(a => a.Role.Id == role.Id).ToList())
// if (old != null)
// DomainService.DeleteObject(old);
var savechecked =
from saved
in checkeditem
where
(
from item
in checkeditem
from par
in checkeditem
where par.Id == item.ParentId
select item).Where(a
=> a.Id == saved.Id).Count() < 1
select saved;

if (savechecked.Count(a => a.Id == Rights.All) > 0)
{
var rrt
= DomainService.RoleRightSet.Where(a => a.Role.Id == roleId).ToList();
foreach (var item in rrt)
DomainService.DeleteObject(item);
DomainService.AddToRoleRightSet(
new RoleRight
{
Id
= Guid.NewGuid().ToString(),
Right
= DomainService.RightSet.Where(a => a.Code == Rights.All).FirstOrDefault(),
Role
= role
});
}
else
{

foreach (var rr in savechecked.Distinct())
{
bool isdel = uncheck.Count(a => a.Id == rr.ParentId) > 0;
if (DomainService.RoleRightSet.Count(a => a.Role.Id == role.Id && (a.Right.Code == rr.Id || (a.Right.Code == rr.ParentId && !isdel))) < 1)
{
var child
=
from item
in DomainService.RoleRightSet
where item.Right.Parent.Code == rr.Id && item.Role.Id == role.Id
select item;
var lst
= child.ToList();
foreach (var item in lst)
if (item != null)
DomainService.DeleteObject(item);

DomainService.AddToRoleRightSet(
new RoleRight
{
Id
= Guid.NewGuid().ToString(),
Right
= DomainService.RightSet.Where(a => a.Code == rr.Id).FirstOrDefault(),
Role
= role
});

}
}
}


DomainService.SaveChanges();
}

 

为了防止万一的不幸错误数据,我还是处理过滤了下。

做这么多无法是为V前方战场做准备的啊。走上战场去:

ContractedBlock.gifExpandedBlockStart.gif代码
<ext:Store ID="dsRoleRights" runat="server" RemoteSort="true" UseIdConfirmation="true"
AutoLoad
="false" ShowWarningOnFailure="false">
<Proxy>
<ext:HttpProxy Url="http://www.cnblogs.com/Data/GetRoleRights/" />
</Proxy>
<UpdateProxy>
<ext:HttpWriteProxy Url="http://www.cnblogs.com/Data/SaveRoleRights/" />
</UpdateProxy>
<Reader>
<ext:JsonReader ReaderID="Id" Root="data" TotalProperty="totalCount">
<Fields>
<ext:RecordField Name="Id" />
<ext:RecordField Name="RightName" />
<ext:RecordField Name="ParentId" />
<ext:RecordField Name="HasCheck" />
</Fields>
</ext:JsonReader>
</Reader>
<BaseParams>
<ext:Parameter Name="roleId" Value="#{txtFilter}.getValue()" Mode="Raw" />
</BaseParams>
<WriteBaseParams>
<ext:Parameter Name="roleId" Value="#{txtFilter}.getValue()" Mode="Raw" />
</WriteBaseParams>
<Listeners>
<Load Fn="bindTree" />
<CommitDone Handler="Ext.MessageBox.alert('操作成功', '角色权限保存成功!');" />
<CommitFailed Handler="Ext.Msg.show({
title: '保存出错。',
msg: '保存角色权限出错!',
buttons: Ext.Msg.OK,
icon: Ext.Msg.ERROR
});"
/>
</Listeners>
</ext:Store>

 

这个是我们前方战场的粮仓,成功与败紧系于它啊。。。具体什么意思,我就不多说了,我们进入主题:

Extjs 来建带Check的树:写代码去。。。。。

给要建的树一个生存之地: 

ContractedBlock.gifExpandedBlockStart.gif代码
<ext:TreePanel ID="treeRight" runat="server" Border="false" AutoScroll="true" BodyStyle="padding-top:5px;">
<Root>
<ext:TreeNode NodeID="all" Text="所有权限" Leaf="false" AllowChildren="true" Expandable="True"
Checked
="false">
</ext:TreeNode>
</Root>
<Listeners>
<CheckChange Fn="nodeCheckchange" />
</Listeners>
<LoadMask ShowMask="true" Msg="正在角色权限..." />
</ext:TreePanel>

建设的开始: 

ContractedBlock.gifExpandedBlockStart.gif代码
var bindTree = function(store, records) {
if (records.length > 0) {
var root = Ext.getCmp('treeRight').root;

var count = records.length;
var r = null
for (var i = 0; i < count; i++) {
if (records[i]['data'].Id == 'all') {
r
= records[i];
break;
}
}
if (r != null) {

removeChildrenRecursively(root);

root.beginUpdate();
//通知UI开始更新节点的操作了
builderNode(root, records);
root.endUpdate();
//alert(r['data'].HasCheck);
if (r['data'].HasCheck)
root.getUI().toggleCheck(
true);
else
root.getUI().toggleCheck(
false);
root.expand(
true);
}
}
}
//清除子节点
function removeChildrenRecursively(node) {
if (!node) return;
while (node.hasChildNodes()) {
removeChildrenRecursively(node.firstChild);
node.removeChild(node.firstChild);
}
}
//构建子节点
var builderNode = function(root, records) {
if (root == null) return;
var count = records.length;
for (var i = 0; i < count; i++) {
r
= records[i];
if (r['data'].ParentId == root.id) {

var flag = false;
for (var j = 0; j < count; j++) {
if (records[j]['data'].ParentId == r['data'].Id) {
flag
= true;
break;
}
}
var node = new Ext.tree.TreeNode({
id: r[
'data'].Id,
text: r[
'data'].RightName,
checked: r[
'data'].HasCheck,
leaf: flag,
expanded:
true,
expandable: flag
})
root.appendChild(node);
if (flag)
builderNode(node, records);

}
}
}

 

 

 也许有人已经发现了<CheckChange Fn="nodeCheckchange" />这句话的nodeCheckchange是何许人物?

对,那就是负责我们Check事件的总动员:让他负责数据的更新及UI的更新,这段代码就是我们实现Check的核心

 

ContractedBlock.gifExpandedBlockStart.gif代码
function setDataSetState(checked, node) {
var records = dsRoleRights.getRange();
var count = records.length;
var i = 0;
for (i; i <= count; i++)
if (records[i]['data'].Id == node.id)
break;
var r = null;
if (count >= i) {
r
= records[i];
if (r['data'].HasCheck != checked) {
//alert(node.id+'数据'+r['data'].HasCheck+',操作'+checked)
r.set('HasCheck', checked);
r.commit(
true);
}
}
}

var flag = true;

var firstId = '';
var isUp = true;
var nodeCheckchange = function(node, checked) {
if (firstId == '')
firstId
= node.id;

if (checked) {
if (firstId = node.id)
setDataSetState(checked, node);
node.eachChild(setNodeChecked);
}
else {
setDataSetState(checked, node);
if (firstId == node.id) {
isUp
= false;
}

if (!isUp) {
node.eachChild(setNodeUnChecked);
}
if (firstId == node.id)
isUp
= true;

if (isUp) {
var parent = node.parentNode;
while (parent != null) {
parent.getUI().toggleCheck(
false);
parent
= parent.parentNode;
}
}
}

if (firstId == node.id)
firstId
= '';
}

var setNodeUnChecked = function(node) {
node.getUI().toggleCheck(
false);
}
var setNodeChecked = function(node) {
node.getUI().toggleCheck(
true);
}

firstId这个变量很重要,它记录了我们发生Check6事件的起始位置。isUp是判断事件是否向上触发。

到此为止我们实现了这样的Check树,及存取操作,你也动手试一试吧,:)干活,干活了。。。

 

转载于:https://www.cnblogs.com/zhouhoujun/archive/2010/05/24/1742752.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值