TreePanel组件(Ext.tree.TreePanel)logogram:Ext.tree.Panel | xtype:treepanel
树与节点
树面板组件的根是源头,从根上拓展出其它的子节点和叶子节点,前者可以继续拓展出子节点,后者因为是叶子,所以不再有子节。Json自身就类似树结构,所以使用Json作为树面板的data是顺理成章的事。节点都来自Ext.data.NodeInterface接口,该接口提供了对节点的创建、删除、替换、查找、获取、插入等操作。而且Ext.data.NodeInterface对象与Ext.data.Model对象之间存在可交换使用的属性、方法,所以前者不但是节点同时也是一个记录对象。也即你可以把节点看成Ext.data.Model对象,可以同时使用两者所具备的属性或方法。
树.配置
//配置数据存储器。配置root指定节点数据。
root :节点数据
//节点数据,如果配置了store就不需要再配置此项,两项任选其一。
rootVisible: bool
//是否显示根节点。
renderTo: Element | Dom ID | Dom
//渲染到哪个元素里。
enableDD: bool
//是否可拖动节点到其它任意节点中。
lines: bool
//是否显示树的层次线
selModel:Ext.selection.Model
//选择模式,参看:选择模式
collapsible:bool
//是否在树的标题栏显示一个折叠展开的按钮用于折叠或展开整棵树
trackMouseOver: bool
//鼠标划过节点时是否显示背景色
useArrows:bool
//节点前的折叠展开图标使用vista风格的三角形图标
columns:[]
//与GridPanel的columns用法完全相同,此项主要用于将树显示为表格树
displayField: string
//默认树节点的文本显示的是text字段,此项可以指定其它的字段用于显示节点文本
valueField: string
////默认树节点的值是id字段,此项可以指定其它的字段作为节点的值
folderSort:bool
//是否排序
hideHeaders:bool
//当树为表格树时是否显示列头
树.方法
//获取根节点,返回Ext.data.NodeInterface
expand(bool)
//bool:是否开启动画效果
//展开树
expandPath(idPath)
//idPath:节点id的路径
//展开处于参数指定的路径的节点
//示例:
//将展开根节点01下的0101子节点
tree.expandPath("/01/0101");
selectPath(idStrPath)
//与expandPath类似,但同时会选中节点的复选框。
collapse(direction, bool)
//折叠树
//direction:折叠的方向,可能的值如下:
//Ext.Component.DIRECTION_TOP
//Ext.Component.DIRECTION_RIGHT
//Ext.Component.DIRECTION_BOTTOM
//Ext.Component.DIRECTION_LEFT
//bool:是否开启动画效果
collapseAll()
//折叠所有节点
disable(bool)
//启用或禁用树
getChecked()
//获取选中的节点,返回Ext.data.NodeInterface[]
getSelectionModel()
//获取选择模式对象,可调用isSelected(record)方法测试某节点是否被选中
getView()
//获取视图
nextNode()
//返回在树的遍历顺序的下一个节点组件,返回Ext.Component。
nextSibling()
//返回此组件的下一个同级组件
previousNode()
//返回在树的遍历顺序的前一个节点的组件.
previousSibling()
//返回此组件的前一个同级组件
树.事件
//view:视图
//node:当前被点击的节点
//节点被点击时触发
checkchange:fn(node, isChecked)
//node:复选框状态改变的节点
//isChecked:true或false
//节点的复选框状态改变时触发
beforeitemexpand: fn(node)
//node:当前被展开的node
//展开节点时触发
beforeitemcontextmenu: fn(view, record, item, index, e)
//右击某项时触发
//示例:
listeners: {
beforeitemcontextmenu: function ( view, record, item, index, e) {
e.preventDefault(true);
alert(record.get("id"));
}
}
节.配置
//节点的标识
text: string
//节点名称
checked: bool
//在当前节点显示复选框且是否选中。
expandable: bool
//是否允许节点有展开折叠的功能
expanded:bool
//展开或折叠当前节点,true为展开
qtip: string
//鼠标滑过当前节点显示的提示信息
qtitle: string
//提示信息的标题
singleClickExpanded: bool
//是否单击节点时就展开这个节点(+-或箭头都是单击时展开,但节点被单击时默认是展开的,可设为false即不展开,只有双击才会展开)
leaf: bool
//是否是叶子,此项影响到节点前的+-图标的显示,如果为false则显示图标,默认为false。还影响字节的显示,如果此项为true,但同时又配置了当前节点的children,那么children就不会显示出来。
children: [Ext.data.Model实例, ……]
//子节点,此项用于配置子节点,但如果要获取子节点应使用childNodes属性。
alllowDrag: bool
//是否允许拖动自身
allowDrop: bool
//是否允许接收来自拖动的节点
href:stringUrl
//指示链接地址
hrefTarget
//指示如何打开链接
节.属性.property
//获取当前节点的所有子节点,返回代表子节点的Object数组,可通过在迭代时get到节点的属性
firstChild
//获取当前节点的第一个子节点
lastChild
//获取当前节点的最后一个子节点
previousSibling
//获取紧靠在当前节点前的第一个兄弟节点
nextSibling
//获取紧靠在当前节点后的第一个兄弟节点
isFirst
//当前节点是否是其父节点所包含的第一个子节点
isLast
//当前节点是否是其父节点所包含的最后一个子节点
parentNode
//获取当前节点的父节点
parentId
//获取当前节点的父节点的id
index
//获取当前节点在其父节点中所处的索引
depth
//获取深度级别
//节没有提供测试是否自身是否被选中,可使用树的getChecked(0方法或使用树的getSel
节.方法
//在当前节点的末尾添加新节点
expand(bool)
//bool:为true时将自动递归展开所有子节点
//展开
isExpanded()
//是否是展开状态
collapse()
//折叠
collapseChildren()
//折叠当前节点的所有子节点
createNode()
//创建节点,占时没搞懂怎么创建后加入tree
destroy()
//自毁
eachChild(fn)
//遍历当前节点的所有子节点,在函数的任何位置返回false则会终止遍历
findChild(property, value, deep)
//property:属性名
//value:属性值
//deep:深度级别,如果为true则查找范围包括后代节点
//查找具备参数指定的属性和属性值的第一个子节点,返回 Ext.data.NodeInterface
findChildBy(fn, scope, deep)
//fn:如果某节点匹配函数的规则,必须返回true
//scope:作用域
//deep:深度级别,如果为true则查找范围包括后代节点
//通过自定义函数查找匹配的第一个子节点,返回 Ext.data.NodeInterface
getChildAt(index)
//获取指定索引处的子节点,返回 Ext.data.NodeInterface
getDepth()
//获取当前节点的深度级别
getPath()
//获取当前节点相对于根节点的层级路径
hasChildNodes()
//是否具有子节点
indexOf(childNode)
//在当前节点中获取参数指定的子节点的索引
indexOfId(string)
//在当前节点中根据参数指定的ID获取子节点
insertBefore(x, y)
//x | y:当前节点的子节点
//在当前节点的子节点中,将x插入到y之前
insertChild(index, node)
//将节点插入到当前节点的子节点的索引指定处
isLeaf()
//是否是叶子节点
isRoot()
//是否是根节点
remove(bool)
//移除自身,如果bool为true,则直接销毁。
removeAll()
//移除所有子节点
removeChild(childNode, bool)
//移除参数指定的子节点,如果bool为true则直接销毁
replaceChild(new,old)
//在当前节点中,用new替换old
树的例子
title: "Simple Tree",
renderTo: Ext.getBody(),
width: 200,
height: 150,
root: {
text: "根",
children: [
{ text: "哲学", leaf: false },
{ text: "数理", leaf: false },
{ text: "英语", leaf: false },
]
}
});
动态添加节点
var childNode = { Text: addValue, ParentID: parentID };
fatherNode.appendChild(childNode);
动态加载树
store专门有一个叫做Ext.data.TreeStore的数据存储器,它对树面板提供了支持,可以使用proxy请求服务端的数据。通常我们读取的树的节点数据都来自数据库的表,而表字段名称不一定就和text、leaf、parentId完全一样,这样就需要创建本地数据模型(Ext.data.Model)匹配数据库表的字段,通过Ext.data.TreeStore这个专门针对动态加载树的存储器去请求服务端的数据。那么假如服务端的数据并没有text这样的字段,树又如何显示节点名称呢?只需要配置树的displayField和valueField就OK,前者指定节点显示的名称是服务端数据的哪个字段,后者指定节点id或者父id,这由你自己决定。以下创建了一棵动态加载数据的树,首次Ajax加载只获取第一层节点,然后在proxy上注册了侦听,当某节点被展开时proxy会继续发起Ajax请求并将PID的值设为当前被展开节点的ID。服务端request到这个值再从数据库查询出其下的子节点,通过JsonCovert将记录转换位字符串输送到客户端。这样,proxy会自动将子节点绑定到被展开的那个节点下面,不需要我们做额外的工作。
数据库
客户端
Ext.define("trees", {
extend: "Ext.data.Model",
//对应数据库表的字段
fields: ['ID', "Text", "ParentID"]
});
//创建Store
var treeStore = Ext.create("Ext.data.TreeStore", {
model: "trees",
proxy: {
type: "ajax",
url: "treeHandler.ashx",
extraParams: { PID: 0 },
reader: {
type: 'json',
root: 'ds'
}
},
listeners: {
beforeexpand: function (node) {
// 因为根节点是服务端JsonConvert返回的数据,root是ds
// ds是不需要显示的,所以首次渲染树时就会自动展开树
// 此时node是不确定的,直到用户展开了树才能捕获被展开的节点
if (node.get('ID')) { this.proxy.extraParams.PID = node.get('ID'); }
}
},
//自动加载数据,即不需要在后面load了
autoLoad: true
});
//创建树
var MyTree = Ext.create("Ext.tree.Panel", {
renderTo: Ext.getBody(),
width: 220,
title: 'simpleDataTree',
store: treeStore,
useArrows: true,
rootVisible: false,
displayField: "Text",
valueField: "ParentID"
})
服务端
{
context.Response.ContentType = "text/plain";
string PID = context.Request["PID"];
DataSet ds = Trees.GetList( "ParentID=" + PID );
if (ds.Tables.Count == 0) { return; }
string nodeList = JsonConvert.SerializeObject( ds );
context.Response.Write( nodeList );
context.Response.End( );
}
递归树
title: "Simple Tree",
renderTo: Ext.getBody(),
width: 200,
height: 150,
store: store
});
var px = 100;
function printMessage(node, px) {
var msg = "<div style='margin-left:" + String(px) + "px;'>" + node.get("id") + ":" + node.get("text") + "</div>"
var p = document.createElement("p");
p.innerHTML = msg;
document.body.appendChild(p);
if (node.childNodes) {
for (var i = 0; i < node.childNodes.length; i++) {
px += (i > 0 ? 0 : 100);
printMessage(node.childNodes[i], px);
}
}
}
printMessage(tree.getRootNode(), px);
表格树(自定义字段)
可以自定义节点的属性,这需要在数据模型上定义字段。,然后在树中使用xtype: "treecolumn"将树配置为表格树。
extend: "Ext.data.Model",
fields: [
{ name: "text", type: "string" },
{ name: "myText", type: "string" }
],
});
var store = new Ext.data.TreeStore({
model: "treeModel",
root: {
text: "根",
id: "01",
myText: "sencha",
children: [
{ text: "哲学", id: "0101", myText: "philosophy", leaf: true },
{ text: "数理", id: "0102", myText: "mathematics", leaf: true },
{ text: "英语", id: "0103", leaf: true, myText: "english", leaf: true }
]
}
});
var tree = new Ext.tree.TreePanel({
title: "Simple Tree",
renderTo: Ext.getBody(),
width: 350,
height: 250,
enableColumnResize: true,
columns: [
{ xtype: "treecolumn", text: "text", dataIndex: "text" },
{ text: "myText", dataIndex: "myText" }
],
store: store,
listeners: {
itemclick: function (view, node) {
Ext.Msg.alert('', node.get("myText"));
}
}
});
复选框树
以下代码结合了复选框事件,当某个父节点的复选框的状态发生改变的时候,通过函数递归将其子节点的复选框状态与父节点的复选框状态设为一致。以下是在每个节点中配置了checked,你也可以在TreePanel例统一配置选择模型为复选框模式。即 selModel:new Ext.selection.CheckboxModel({checkOnly:false})
root: {
text: "根",
id: "01",
checked: false,
children: [
{
text: "哲学", id: "0101", leaf: false, checked: false,
children: [
{
text: "测试", id: "010101", leaf: false, checked: false,
children: [
{ text: "测试", id: "01010101", leaf: true, checked: false },
{ text: "测试", id: "01010102", leaf: true, checked: false }
]
},
{ text: "测试", id: "010102", leaf: true, checked: false }
]
},
{ text: "数理", id: "0102", leaf: true, checked: true },
{ text: "英语", id: "0103", leaf: true, checked: false }
]
}
});
var tree = new Ext.tree.TreePanel({
title: "Simple Tree",
renderTo: Ext.getBody(),
width: 250,
height: 250,
store: store,
listeners: {
checkchange: function (node, isChecked) {
if (node.childNodes.length > 0) { changeChecked(node.childNodes, isChecked) }
//嵌套了递归函数
function changeChecked(childs, isChecked) {
for (var index in childs) {
childs[index].set("checked", isChecked);
if (childs[index].childNodes.length > 0) {
arguments.callee(childs[index].childNodes, isChecked);
}
}
};
}
}
});
无限极下拉树
extend: "Ext.form.field.Picker",
//注册自定义组件的alias,即为组件绑定xtype,此处xtype为comboboxtree
alias: "widget.comboboxtree",
requires: ["Ext.tree.Panel"],
textVal: [],
hideVal: [],
paVal:[],
initComponent: function () {
var self = this;
Ext.apply(self, {
fieldLabel: self.fieldLabel,
labelWidth: self.labelWidth
});
self.callParent();
},
//查找被选中节点的祖先节点,只返回第一层的ID
geterRootChildID: function (r, id) {
while (true) {
if (r.get("parentid").tostring() == "") {
return id;
}
id = r.get("id");
r = r.parentnode;
}
},
//查找该节点上的所有被选中的祖先节点中最上层的那个节点;
getGrandaStateID: function (r) {
var self = this;
var selction = self.picker.getSelectionModel();
var id = "NAN";
while (true) {
if (r.get(self.paValueField).toString() == "") {
return id;
}
if (selction.isSelected(r)) {
id = r.get(self.valueField);
}
r = r.parentNode;
}
},
checkControl: function () {
var self = this;
self.textVal = [];
self.hideVal = [];
var records = self.picker.getSelectionModel().selected;
records.each(function (record) {
self.textVal.push(record.get(self.displayField)); // key
self.hideVal.push(record.get(self.valueField)); // value
});
self.setValue(self.textVal.join(',')); // 显示给用户看到的文本
},
createPicker: function () {
var self = this;
self.picker = new Ext.tree.Panel({
height: 300,//不要在外部配置comboboxtree的height
autoScroll: true,
floating: true,
focusOnToFront: false,
shadow: true,
ownerCt: this.ownerCt,
useArrows: true,
store: self.store,
rootVisible: false,
displayField: self.displayField,
valueField: self.valueField,
paValueField: self.paValueField,
getPa: self.getPa,
selModel: self.selModel,
resizable: self.resizableTree
});
self.picker.on({
//选择发生变化时,此时需要将选中后又取消了选择的节点从文本框、textVal、hideVal中移除
selectionchange: function () {
self.checkControl();
},
deselect: function (selmodel, deRecord, ) {
var index = self.paVal.indexOf(deRecord.get(self.valueField));
if (index != -1) {
//从数组中移除自身权限
self.paVal.splice(index, 1);
}
var selction = self.picker.getSelectionModel();
//如果子节有权限,则将自身权限移交子节
if (deRecord.hasChildNodes()) {
var chids = deRecord.childNodes;
chids.forEach(function (rec) {
if (selction.isSelected(rec)) {
self.paVal.push(rec.get(self.valueField));
}
});
}
},
select: function (tree, record, item, index, e, options) {
//多选
if (self.nodeMoreSelect) {
self.checkControl();
}
//单选
else {
self.textVal = [];
self.hideVal = [];
self.textVal.push(record.get(self.displayField)); //key
self.hideVal.push(record.get(self.valueField)); // value
self.setValue(record.get(self.displayField)); // 显示给用户看到的文本
}
//权限模式,只获取被赋予了权限的节点其上层的权限,上层的权限必然高于子节点的权限
if (self.getPa) {
var selction = self.picker.getSelectionModel();
if (record.hasChildNodes()) {
var chids = record.childNodes;
//赋予了当前被点击节点的权限则应删除当前节点的所有子节的权限
chids.forEach(function (rec) {
var index = self.paVal.indexOf(rec.get(self.valueField));
if (index != -1) {
self.paVal.splice(index, 1);
}
});
}
var selectedPaID = self.getGrandaStateID(record);
var index = self.paVal.indexOf(record.get(self.valueField));
//赋予了当前节点的祖先节点权限则将祖先节点的权限添加到数组,同时从数组中移除自身的权限
if (selectedPaID != "NAN") {
var PaIndex = self.paVal.indexOf(selectedPaID);
if (PaIndex == -1) {
self.paVal.push(selectedPaID);
}
if (index != -1) {
self.paVal.splice(record.get(self.valueField),1);
}
}
//否则将自身权限添加到数组
else {
self.paVal.push(record.get(self.valueField));
}
}
}
});
return self.picker;
},
alignPicker: function () {
var me = this,
picker, isAbove, aboveSfx = '-above';
if (this.isExpanded) {
picker = me.getPicker();
if (me.matchFieldWidth) {
picker.setWidth(me.bodyEl.getWidth());
}
if (picker.isFloating()) {
picker.alignTo(me.inputEl, "", me.pickerOffset); // ""->tl
isAbove = picker.el.getY() < me.inputEl.getY();
me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
picker.el[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
}
}
}
});
引入扩展类后再创建它的实例
//创建model
Ext.define("trees", {
extend: "Ext.data.Model",
//对应数据库表的字段
fields: ['ID', "Text", "ParentID"]
});
//创建Store
Ext.create("Ext.data.TreeStore", {
storeId: "comboStore",
model: "trees",
proxy: {
type: "ajax",
url: "AdminColumnHandler.ashx",
extraParams: { PID: 0 },
reader: {
type: 'json',
root: 'ds'
}
},
listeners: {
beforeexpand: function (node) {
if (node.get('ID')) { this.proxy.extraParams.PID = node.get('ID'); }
}
},
autoLoad: true
});
Ext.onReady(function () {
Ext.create("Ext.Yin.comboboxtree", {
id: 'combo',
width: 270,
fieldLabel: '无限极下拉树',
labelWidth: 100,
store: "comboStore",
displayField: "Text",//下拉框数据模型(data.Model)中的key
valueField: "ID",//下拉框数据模型(data.Model)中的value
paValueField: "ParentID",//可选项,下拉框数据模型(data.Model)中的父ID字段名,配置此项后会得到一个包含上层权限ID的数组,如果有子节被选中,则子节ID不会存入数组,因为上层权限高于子节。
renderTo: Ext.getBody(),
selModel: new Ext.selection.CheckboxModel({ checkOnly: false, mode: "SIMPLE" }),//复选框模式,如果多选 mode改为SIMPLE,单选改为SINGLE
nodeMoreSelect: true,//是否支持多选
resizableTree: true,//是否可拖拽调整大小
});
});
var nodeNames = form.findField("combo").textVal;//获取被选中的key,返回key的数组,无key时length为0
var nodeIDs = form.findField("combo").hideVal;//获取被选中的value,返回value的数组,无value时length为0
var grandIDs = form.findField("combo").paVal;//获取被选中的value,返回value的数组,此数组虽然存储了被选中的节点的ID,但如果节点的某上层节点被选中则只返回上层节点的ID,无value时length为0
xtype: comboboxtree//也可以通过xtype配置下拉树
});
添加删除节点
客户端
//统一验证提示信息
Ext.QuickTips.init();
Ext.form.TextField.prototype.msgTarget = "title";
if (Ext.getCmp("addWin")) { return; }
//树模型
Ext.define("TreeDataModel", {
extend: "Ext.data.Model",
fields: [
"ID", "Text", "ParentID"
]
});
//树的存储器
Ext.create("Ext.data.TreeStore", {
storeId: "TreeDataStore",
model: "TreeDataModel",
autoLoad: true,
proxy: {
type: "ajax",
url: "AdminColumnHandler.ashx",
extraParams: { PID: 0 },
reader: {
type: 'json',
root: 'ds'
}
},
listeners: {
beforeexpand: function (node) {
if (node.get("ID")) {
this.proxy.extraParams.PID = node.get("ID");
}
}
}
});
//展开树的按钮
Ext.create("Ext.button.Button", {
text: "选择父栏目", style: "background:red;border:none;margin:10px 0;", icon: "../Image/expand-down.png",
renderTo: "expandTree",
handler: function () {
this.border = false;
var treepanel = Ext.getCmp("dataTree");
if (!window.counter) { window.counter = 1; }
if (window.counter % 2 == 0) {
treepanel.show();//显示树
this.setIcon("../Image/expand-down.png");
}
else {
treepanel.hide();//隐藏树
this.setIcon("../Image/expand.png");
}
window.counter += 1;
}
});
//树
Ext.create("Ext.tree.Panel", {
id: "dataTree",
title: "columnItem",
frame: true,
useArrows: true,
rootVisible: false,
displayField: "Text",
valueField: "ParentID",
renderTo: "dataTree",
store: Ext.getStore("TreeDataStore"),
listeners: {
beforeitemcontextmenu: function (view, node, item, index, e) {
e.preventDefault();
Ext.create("Ext.menu.Menu", {
id: "contextMenu",
border: false,
listeners: {
//右键菜单失去激活后应销毁它
deactivate: function () {
this.destroy();
}
},
items: [
{
text: "编辑", icon: "../Image/edit_16px.ico",
handler: function () {
var updateText = node.get("Text");
var updateId = node.get("Id");
//创建表单
Ext.create("Ext.form.Panel", {
id: "formEdit",
style: "padding-top:10px;",
border: false,
defaultType: "textfield",
items: [
{
fieldLabel: "", name: "inputText", labelWidth: 40, emptyText: node.get("Text"),
validator: function (va) {
var valueLen = $(document).getStrLen(va);
if ($(document).TestEmpty(va)) { return "不能有空值" }
if (valueLen < 4) { return "不能少于4个字符,中文算两个字符" }
if (valueLen > 50) { return "不能大于50个字符,中文算两个字符" }
return true;
}
}
],
buttons: [
{
text: "", style: "background:none;border:none;color:black;", icon: "../Image/cancel.ico", maxWidth: 22,
handler: function () {
Ext.getCmp("winEdit").destroy();
}
},
{
text: "", style: "background:none;border:none;color:black;", icon: "../Image/yes.ico", maxWidth: 22,
handler: function () {
var form = Ext.getCmp("formEdit").getForm();
var v = form.findField("inputText").getValue();
if (form.isValid()) {
Ext.getCmp("winEdit").destroy();
Ext.Ajax.request({
method: "post",
url: "AdminColumnHandler.ashx",
params: { update: "true", updateId: node.get("ID"), updateText: v },
success: function (r) {
var m = Ext.decode(r.responseText);
Ext.Msg.alert("提示", m.msg);
//更新成功则在不刷新页面的情况下立即更改节点的Text以便马上可以看到修改后的效果
if (m.status == "1") {
node.set("Text", v);
node.commit();
}
},
failure: function () {
Ext.Msg.alert("提示", "http错误");
}
});
}
else {
Ext.Msg.alert("", "未通过验证");
}
}
}
]
});
//创建弹出层
Ext.create("Ext.window.Window", {
id: "winEdit",
baseCls: 'my-panel-no-border',
border: false, shadow: false, resizable: false, closable: false, draggable: false,
items: Ext.getCmp("formEdit")
});
//弹出层显示的坐标点
Ext.getCmp("winEdit").showAt(e.getPoint());
}
},
{
text: "添加子项", icon: "../Image/add_16px.ico",
handler: function () {
var addText = node.get("Text");
var parentId = node.get("Id");
//创建表单
Ext.create("Ext.form.Panel", {
id: "formEdit",
style: "padding-top:10px;",
border: false,
defaultType: "textfield",
items: [
{
fieldLabel: "", name: "inputText", labelWidth: 40, emptyText: "新栏目名称",
validator: function (va) {
var valueLen = $(document).getStrLen(va);
if ($(document).TestEmpty(va)) { return "不能有空值" }
if (valueLen < 4) { return "不能少于4个字符,中文算两个字符" }
if (valueLen > 50) { return "不能大于50个字符,中文算两个字符" }
return true;
}
}
],
buttons: [
{
text: "", style: "background:none;border:none;color:black;", icon: "../Image/cancel.ico", maxWidth: 22,
handler: function () {
Ext.getCmp("winEdit").destroy();
}
},
{
text: "", style: "background:none;border:none;color:black;", icon: "../Image/yes.ico", maxWidth: 22,
handler: function () {
var form = Ext.getCmp("formEdit").getForm();
var v = form.findField("inputText").getValue();
var parentId = node.get("ID");
if (form.isValid()) {
Ext.getCmp("winEdit").destroy();
Ext.Ajax.request({
method: "post",
url: "AdminColumnHandler.ashx",
params: { add: "true", parentId: parentId, addText: v },
success: function (r) {
var m = Ext.decode(r.responseText);
Ext.Msg.alert("提示", m.msg);
//更新成功则在不刷新页面的情况下立即创建新节点以便马上可以看到效果
if (m.status == "1") {
var childNode = {
Text: v,
ParentID: parentId
};
node.appendChild(childNode);
node.commit();
}
},
failure: function () {
Ext.Msg.alert("提示", "http错误");
}
});
}
else {
Ext.Msg.alert("", "未通过验证");
}
}
}
]
});
//创建弹出层
Ext.create("Ext.window.Window", {
id: "winEdit",
baseCls: 'my-panel-no-border',
border: false, shadow: false, resizable: false, closable: false, draggable: false,
items: Ext.getCmp("formEdit")
});
//弹出层显示的坐标点
Ext.getCmp("winEdit").showAt(e.getPoint());
}
}
]
});
if (Ext.getCmp("contextMenu")) { Ext.getCmp("contextMenu").showAt(e.getPoint()); }
}
}
});
});
服务端
{
public void ProcessRequest( HttpContext context )
{
context.Response.ContentType = "text/plain";
string PID = context.Request["PID"];
string update = context.Request["update"];
string add = context.Request["add"];
string msg = "更新失败";
string status = "0";
//栏目树
if (!string.IsNullOrEmpty( PID ))
{
DataSet ds = ColumnItem.GetList( "parentId=" + PID );
string treeDataStr = JsonConvert.SerializeObject( ds );
context.Response.Write( treeDataStr );
}
//更新栏目
else if (!string.IsNullOrEmpty( update ))
{
int ID = int.Parse( context.Request["updateId"].ToString( ) );
string Text = context.Request["updateText"];
ColumnItem c = new ColumnItem( ID );
c.Text = Text;
try
{
c.Update( );
msg = "更新成功";
status = "1";
}
finally
{
context.Response.Write( "{msg:' " + msg + " ',status:'" + status + "'}" );
}
}
//增加栏目
else if (!string.IsNullOrEmpty( add ))
{
int parentId = int.Parse( context.Request["parentId"].ToString( ) );
string addText = context.Request["addText"];
ColumnItem c = new ColumnItem( );
c.Text = addText;
c.ParentID = parentId;
try
{
c.Add( );
msg = "添加成功";
status = "1";
}
finally
{
context.Response.Write( "{msg:' " + msg + " ',status:'" + status + "'}" );
}
}
context.Response.End( );
}
}
其它问题
1.删除节点图标
在每一个节点上配置一个iconCls : 'no-icon',使用css统一祛除:.no-icon {display:none;}。
2.复选框实现单选
Ext.selection.CheckboxModel本身有这个单选的配置,selModel: new Ext.selection.CheckboxModel({ checkOnly: false, mode: "SINGLE" })。