好大一棵樹,新春的祝福(二):功能節點的資料結構和頁面展示
1、資料結構
在原有的基礎上,把noteID改成FunctionID,去掉code欄位,增加三個欄位。
NoteLevel :表示第幾級的節點,可以和css配合,“美化”顯示效果。 ParentIDPath: 父節點的路徑,用於找到一個節點的子節點和子子節點(及所有子節點)。也可以找到一個節點的所有父節點。 OrderID :所有節點的總排序,大家一起來排序,一個SQL語句就可以提取出來直接繫結控制元件,而不需要在使用遞迴了。
由於用功能節點作為例子,所以再增加兩個欄位
WebURL: 開啟網頁的網址 Target : 開啟網頁的目標
【表結構的截圖】
雖然使用三個欄位才實現了原來的一個欄位的功能,但是每個欄位的分工都很明確,也更“專業”,當然你也可以說這三個都是冗餘欄位。
2、提取資料
由於節點的縮排效果不用空格來佔位了,所以這裡的提取資料的SQL就簡單很多了。
SELECT FunctionID, NoteTitle, NoteLevel, ParentIDPath, WebURL, Target FROM Test_Tree2 ORDER BY OrderID
【提取資料用的SQL語句】
還是一條SQL語句,由於OrderID是所有節點的總排序,所以得到記錄集之後直接繫結控制元件就可以了,不需要使用遞迴。當然這裡說的控制元件不是TreeView,而是Repeater、GridView等。
【使用OrderID欄位排序的效果】
3、如何來顯示?(請CSS來幫忙)
為什麼放著好好的 TreeView 不用呢?因為他不是太靈活,不好控制頁面的顯示,如果美工做得效果太特殊了,那麼就不好弄了。
對於“單列”的樹,我習慣使用Repeater來顯示,內部採用DIV。而對於“多列”的樹,我們可以使用GridView控制元件。GridView控制元件的樹狀結構在下一篇(許可權選擇)裡面來說明。
使用 Repeater 也是很簡單的。
.aspx裡的程式碼:
<asp:Repeater ID="Rpt" runat="server"> <ItemTemplate> <div id='n<%# (Container.DataItem as DataRowView)["FunctionID"]%>'><%# (Container.DataItem as DataRowView)["noteTitle"]%></div> </ItemTemplate> </asp:Repeater>
.aspx.cs裡面的程式碼:
private void BindTree()
{
//提取資料
string sql = "SELECT FunctionID, NoteTitle, NoteLevel, ParentIDPath, WebURL, Target FROM Test_Tree2 ORDER BY OrderID";
DataTable dt = dal.RunSqlDataTable(sql);
Rpt.DataSource = dt;
Rpt.DataBind();
輸出js指令碼需要的js陣列#region 輸出js指令碼需要的js陣列
if (dt.Rows.Count > 0)
{
System.Text.StringBuilder str = new System.Text.StringBuilder();
str.Append("<script language="javascript">var notes = new Array();");
//開啟指定結點的子結點 還未實現功能
str.Append("var firstID = " + dt.Rows[0][0].ToString() + " ;rn");
str.Append("var lastID = " + dt.Rows[dt.Rows.Count - 1][0].ToString() + " ;rn");
str.Append("var a = notes;rn");
foreach (DataRow dr in dt.Rows)
{
str.Append("a[");
str.Append(dr["FunctionID"].ToString()); //FunctionID
str.Append("] = new Array('");
str.Append(dr["NoteLevel"].ToString()); //noteLevel
str.Append("','");
str.Append(dr["ParentIDPath"].ToString()); //path
str.Append("','");
str.Append(dr["WebURL"].ToString()); //weburl
str.Append("','");
str.Append(dr["Target"].ToString()); //target
str.Append("','");
str.Append(dr["NoteLevel"].ToString() == "1" ? "1" : "0"); //isshow
str.Append("')rn");
}
str.Append("</script>");
Response.Write(str.ToString());
}
#endregion
}
【Repeater的前臺和後臺的程式碼】
這樣倒是可以顯示出來,但是也太難看了,根本就區分不出來一級節點和二級節點,這樣拿出來還不得被罵s。彆著急我們可以請css來“美化”一下。
增加了class屬性和滑鼠跟隨和滑鼠單擊的Repeater的程式碼:
<asp:Repeater ID="Rpt" runat="server">
<ItemTemplate>
<div style="display:block" id='n<%# (Container.DataItem as DataRowView)["FunctionID"]%>' class='css_Tree<%# (Container.DataItem as DataRowView)["NoteLevel"]%>' onclick="treeClick(this)" onmouseover="treeOver(this)" onmouseout="treeOut(this)">
<%# (Container.DataItem as DataRowView)["noteTitle"]%></div>
</ItemTemplate>
</asp:Repeater>
</div>
CSS的程式碼:
.css_Tree1
{
PADDING: 3px 1px 1px 10px ;
FONT-WEIGHT: bold;
FONT-SIZE: 16px;
COLOR: #222222;
LINE-HEIGHT: 150%;
HEIGHT: 20px;
background-color:#ddddda;
}
.css_Tree2
{
PADDING: 4px 1px 1px 30px ;
FONT-SIZE: 12px;
COLOR: #111111;
height:18pt;
background-color:#f6f6f6;
}
.mySelectTree1
{
PADDING: 3px 1px 1px 10px ;
FONT-WEIGHT: bold;
FONT-SIZE: 16px;
LINE-HEIGHT: 150%;
HEIGHT: 20px;
border: 0px solid #369;
background-color: #ddddff;
color: #000;
}
.mySelectTree2
{
border: 1px solid #369;
background-color: #FFFFCF;
color: #111;
PADDING: 4px 1px 1px 30px ;
FONT-SIZE: 12px; height:18pt;
}
.myActiveTree2
{
PADDING: 4px 1px 1px 30px ;
FONT-SIZE: 12px;
COLOR: #111111;
height:18pt;
border: 1px solid #369;
background-color: #CBDDEE;
}
【css的程式碼】
【效果截圖】(好像還是挺難看的)
怎麼樣?這樣可以看得過去了吧。當然我是一點藝術細胞都沒有的人,弄出來的css也是很難看,不過沒關係,我們可以請專業人士來寫出來漂亮的css。
優點:只要是可以用css表現出來的效果都可以加在這個“樹”上面,而所需要做得只是修改一下css檔案,而不用改程式碼。
4、如何展開收攏(js指令碼)
總算是好看了一點,但是現在任何效果都沒有哇,至少也得弄出來個展開收攏的效果呀。
所謂的收攏嘛,其實就是讓子節點不可見,讓網頁裡的標籤不可見的話,可以使用style="display:none" 來做到,可見的話可以使用style="display:block"。
我們可以給 div 加一個onclick事件,在事件裡面修改子節點的display的值。
思路很簡單,程式碼也很簡單。(程式碼在上面)
5、加上滑鼠跟隨和選中的效果
這個還是js指令碼來實現,給div加上 onclick、onmouseover、 onmouseout 事件,然後在事件裡面修改div的Class就可以了。
var oldDiv_Over
var oldDiv_Active
var oldCss1
var oldCss2
var oldCss_Over
function treeClick(me)
{
var id = me.id;
id = id.substring(1);
var path = me.path;
//alert(notes[id][2]);
if (notes[id][2] == "#")
{
//一級節點,展開、收攏子節點。
if (notes[id][4] == "1")
{
//隱藏子節點
ShowNote(id,0);
notes[id][4] = "0"
}
else
{
//顯示子節點
ShowNote(id,1);
notes[id][4] = "1"
}
}
else
{
//二級節點,開啟網頁
var url = notes[id][2] + "?fid=" + notes[id][0] ;
window.open(url ,notes[id][3]);
if (oldDiv_Active)
oldDiv_Active.className = oldCss2 ;
oldDiv_Active = me;
oldCss2 = me.className;
me.className = "myActiveTree2";
}
}
function ShowNote(ParentID,isShow)
{
for (i=1;i<=lastID;i++)
{
if (notes[i])
{
if (notes[i][0] == "2" )
{
path = notes[i][1];
if (path.indexOf("0," + ParentID) == 0 )
{
if (isShow == "0")
document.getElementById("n" + i).style.display = "none";
else
document.getElementById("n" + i).style.display = "block";
}
}
}
}
}
function treeOver(me)
{
oldDiv_Over = me;
oldCss_Over = me.className;
var id = me.id;
id = id.substring(1);
if (oldDiv_Over != null && oldDiv_Active != oldDiv_Over)
if (notes[id][0] == "1")
me.className = "mySelectTree1";
else
me.className = "mySelectTree2";
}
function treeOut(me)
{
if (oldDiv_Over != null && oldDiv_Active != oldDiv_Over)
oldDiv_Over.className = oldCss_Over ;
}
6、小結
這個功能節點的資料結構是n級的,css樣式也可以是n級的,但是js指令碼卻是按照兩級來編寫的,為什麼呢?因為我覺得功能節點這一塊,設定成兩級的是比較合理的,客戶看起來和操作起來都比較舒服,如果級數多了的話,有一點迷宮的感覺了。
什麼?您說您的專案就是很複雜的,二級的不夠必須是三級的,那麼怎麼實現呢?有兩種方法:
第一種是把一級節點放在上面作為導航;第二種是,把三級節點做成標籤的形式。
如果您的專案三級節點也是不行的,必須是四級的,那麼也可以,就是把上面的兩種方法和在一起,一級的節點放在上面作為導航欄,二級、三級的“升一級”放在左面的功能節點裡,四級節點做成標籤的形式。
什麼?四級的還不夠,那我也沒有什麼辦法了。
演示效果,可以單擊節點
樹狀結構的演示
基本的n級分類
加一個“編號”
我的樹
功能演示
新聞管理
員工管理