做网站的,应该经常遇到使用分类功能的,通常有个二级三级分类已经很不错,在实现上,这些也不是很难的。但是对于某些系统,却需要实现更多级分类,例如文章系统,分类级数是不确定的,所以现在网上的系统中基本都采用无限分类。 看到很多地方都有关于无限分类实现的教程,也有人说这个是非常简单的功能。简单与否,在于每个人的能力了。 现在我们也来考察一下如何实现这个无限分类。在这里我采用的是 ASP(VBScript) 结合 Access 数据库来实现。 通常对于分类,我们需要有类别的编号和类别的名称,所以我们在数据库中定义这两个字段,ID 和 Title,ID 字段为自动编号,Title 选择文本类型。 但是如果仅有这两个字段,能实现的恐怕只有一级分类了。多级分类中,不同级之间都会存在关联性,即每个子类别,都应该是唯一地属于某个父类别的,所以在数据库中,我们表现出当前子类别所属的父类别即可。鉴于此,我们增加一个字段ParentID,保存父类别 ID 值。对于根类别,我们可以设置这个值为 0 。 至此,基本上就可以具备了无限级分类所必需的数据。 但是在实际使用中,出于效率问题,我们增加进两个字段: IDPath - 用来保存当前类别的 ID 路径,即从顶层到当前类别的各级分类的 ID ,各级层间用分隔符“|”分隔。 TitlePath - 用来保存当前类别的 Title 路径,即每级分类的标题,各级层间用分隔符“|”分隔。 鉴于是要实现无限分类,上面两个字段的内容可能会很长,所以我们选择备注类型。具体的作用,在后面的程序中可以明了。 这样我们得到完整的数据库: ID - 分类编号 - 自动编号 Title - 分类标题 - 文本 ParentID - 父类别编号 - 整数 IDPath - 分类层次编号路径 - 备注 TitlePath - 分类层次标题路径 - 备注 - 未完待续 -
我们首先来考察如何显示表中已经存在的数据。先给出代码: 代码: Sub listData Dim intSortID : intSortID = CID(Request.QueryString("SortID")) Dim rs, sql Set rs = Server.CreateObject("ADODB.Recordset") sql = "SELECT s.ID, s.Title, s.IDPath, s.TitlePath, (SELECT Count(*) FROM Sorts ss WHERE ss.ParentID = s.ID) AS iCount FROM Sorts s WHERE s.ParentID = " & intSortID & ";" rs.Open sql, conn, 1, 1, 1 If rs.RecordCount > 0 Then Dim arrIDs, arrTitles, intLoop Response.Write("<table width=""100%"" cellpadding=""3"" cellspacing=""1"" border=""0"" bgcolor=""#CCCCCC"">") Response.Write("<tr bgcolor=""#E0E0E0""><th>分类名</th><th>子类别数目</th><th>分类层级</th></tr>") Do While Not rs.EOF Response.Write("<tr bgcolor=""#FFFFFF""><td><a href=""?action=list&SortID=" & rs("ID") & """>" & rs("Title") & "</a> <img src=""images/icon_new_window.gif"" width=""11"" height=""14"" border=""0"" alt=""打开功能菜单"" title=""打开功能菜单"" onclick=""showMenu(event, '', '" & rs("ID") & "');"" /></td><td>" & rs("iCount") & "</td><td><a href=""?action=list&SortID=0"">根</a> » ") ' 数据入库的时候保证 IDPath 和 TitlePath 不为空 arrIDs = Split(rs("IDPath"), "|") arrTitles = Split(rs("TitlePath"), "|") For intLoop = 0 To UBound(arrIDs) If intLoop = UBound(arrIDs) Then ' 如果是最后一级 Response.Write("<a href=""?action=list&SortID=" & arrIDs(intLoop) & """>" & arrTitles(intLoop) & "</a>") Else Response.Write("<a href=""?action=list&SortID=" & arrIDs(intLoop) & """>" & arrTitles(intLoop) & "</a> » ") End If Next Response.Write("</td></tr>") rs.MoveNext Loop Response.Write("</table>") Else Response.Write("暂无子类别,是否<a href=""?action=add&SortID=" & intSortID & """>添加</a>?") End If rs.Close Set rs = Nothing End Sub 我们将列出分类的代码写入一段子程序 listData,其中变量 intSortID 是传递过来的一个类别编号,而我们要显示的是该类别下的子类别。 对于第一级分类,这个编号为 0。 CID() 函数是自定义的函数,用于将字符串转换为 ID 类型的数据,即大于等于 0 的整数,后面还会多次用到这个自定义函数,代码如下: 代码: '-------------------------------------------------------------------------------- Function CID(strS) '-------------------------------------------------------------------------------- ' 转换为有效的 ID ' 返回值类型:Integer (>=0) '-------------------------------------------------------------------------------- Dim intI intI = 0 If IsNull(strS) Or strS = "" Then intI = 0 Else If Not IsNumeric(strS) Then intI = 0 Else Dim intk On Error Resume Next intk = Abs(Clng(strS)) If Err.Number = 6 Then intk = 0 ''数据溢出 Err.Clear intI = intk End If End If CID = intI End Function 数据库操作,就是从表中找到 ParentID = intSortID 的记录,然后循环显示。 在针对 IDPath 和 TitlePath 这两个字段的内容上,定义两个数组 arrIDs 和 arrTitles ,分别循环显示类别层次即可。这里需要注意最后一个层次(在我的代码中,最后一级即为当前分类),其后是不包括箭头标示的,表示已经是当前分类了。 当没有记录的时候,显示提示信息,并有一个增加分类的链接。 因为所有的添加、编辑、删除、列表操作我放在一个页面中,所以链接中有个 action 变量来确定是进行哪步操作。 另外,显示的分类标题后面有个小图标,点击能显示功能菜单,相应的代码在后面给出。 - 未完待续 -