带Check状态的树,功能说明:
1)当一个节点选中时,其子节点也都选中;
2)当一个节点取消选中时,其子节点也都取消选中,其父接点也都需要取消选中
效果见图:
图像上虚框是真正操作的CheckBox
废话不多说,上代码,首先看看例子的数据结构
看到这几张图,也许你有自己做的思路了吧,先看看我做的吧
我看需要查询出权限,还得查询出角色有的权限。何不一起查呢。。


{
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有的,让你看看代码吧:)


{
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; }
}
我们后方资源准备的半足了,好像缺少什么啊,对了存储角色权限啊。继续写代码:


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前方战场做准备的啊。走上战场去:


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的树:写代码去。。。。。
给要建的树一个生存之地:


<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>
建设的开始:


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的核心


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树,及存取操作,你也动手试一试吧,:)干活,干活了。。。