建立系統導覽目錄(利用巢狀的listView)
在google上面看到jquery mobile的巢狀的listview,立刻驚覺這就是我要的系統導覽圖
比起我之前用可折疊的div產出來的畫面好看太多了!([JQM][008][MVC4+JQM]建立系統導覽目錄(利用可折疊的div))
廢話不多說,先直接把官網的例子複製出來用看看:
先新增一個partialview:_TestNestedListView.cshtml,css的部分參考官網的教學直接貼到這個view裡面:
<style id="collapsible-list-item-style">
/* Basic settings */
.ui-li-static.ui-collapsible {
padding: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview,
.ui-li-static.ui-collapsible > .ui-collapsible-heading {
margin: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content {
padding-top: 0;
padding-bottom: 0;
padding-right: 0;
border-bottom-width: 0;
}
/* collapse vertical borders */
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-last-child,
.ui-li-static.ui-collapsible.ui-collapsible-collapsed > .ui-collapsible-heading > a.ui-btn {
border-bottom-width: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-first-child,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-first-child > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn {
border-top-width: 0;
}
/* Remove right borders */
.ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > .ui-li-static,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content {
border-right-width: 0;
}
/* Remove left borders */
/* Here, we need class ui-listview-outer to identify the outermost listview */
.ui-listview-outer > .ui-li-static.ui-collapsible .ui-li-static.ui-collapsible.ui-collapsible,
.ui-listview-outer > .ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content {
border-left-width: 0;
}
</style>
巢狀listView的部分也參照官網直接貼上到這個view裡面:
<ul data-role="listview" class="ui-listview-outer" data-inset="true">
<li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">
<h2>Birds</h2>
<ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">
<li>Condor</li>
<li><a href="#">Eagle</a></li>
<li>Sparrow</li>
</ul>
</li>
<li><a href="#">Humans</a></li>
<li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">
<h2>Fish</h2>
<ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">
<li><a href="#">Salmon</a></li>
<li>Pollock</li>
<li>Trout</li>
</ul>
</li>
<li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">
<h2>Choose your preference</h2>
<form>
<fieldset data-role="controlgroup" data-type="horizontal">
<label>Birds<input type="checkbox" id="choose-birds-regular"></input></label>
<label>Humans<input type="checkbox" id="choose-humans-regular"></input></label>
<label>Fish<input type="checkbox" id="choose-fish-regular"></input></label>
</fieldset>
</form>
</li>
</ul>
然後在Controller加上這段,直接以partialView顯示,才可以避免mvc4套版套到電腦版的layout:
public ActionResult TestNestedListView()
{
ViewBag.Title = "測試巢狀的ListView:";
return PartialView("_TestNestedListView");
}
執行結果:(電腦版+手機版)
直接複製貼上就可使用,官網作的範例還不錯!至少蠻好理解的,只要適當的安排<ul>, <li>的css的class即可
完整的view的內容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<style id="collapsible-list-item-style">
/* Basic settings */
.ui-li-static.ui-collapsible {
padding: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview,
.ui-li-static.ui-collapsible > .ui-collapsible-heading {
margin: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content {
padding-top: 0;
padding-bottom: 0;
padding-right: 0;
border-bottom-width: 0;
}
/* collapse vertical borders */
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-last-child,
.ui-li-static.ui-collapsible.ui-collapsible-collapsed > .ui-collapsible-heading > a.ui-btn {
border-bottom-width: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-first-child,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-first-child > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn {
border-top-width: 0;
}
/* Remove right borders */
.ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > .ui-li-static,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content {
border-right-width: 0;
}
/* Remove left borders */
/* Here, we need class ui-listview-outer to identify the outermost listview */
.ui-listview-outer > .ui-li-static.ui-collapsible .ui-li-static.ui-collapsible.ui-collapsible,
.ui-listview-outer > .ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content {
border-left-width: 0;
}
</style>
<script>
$(document).ready(function () {
$.mobile.ajaxEnabled = false;
});
</script>
</head>
<body>
<div data-role="page" data-theme="a">
@Html.Partial("_ViewSwitcher")
<div data-role="header">
<h1>@ViewBag.Title</h1>
</div>
<div data-role="content">
<ul data-role="listview" class="ui-listview-outer" data-inset="true">
<li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">
<h2>Birds</h2>
<ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">
<li>Condor</li>
<li><a href="#">Eagle</a></li>
<li>Sparrow</li>
</ul>
</li>
<li><a href="#">Humans</a></li>
<li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">
<h2>Fish</h2>
<ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">
<li><a href="#">Salmon</a></li>
<li>Pollock</li>
<li>Trout</li>
</ul>
</li>
<li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">
<h2>Choose your preference</h2>
<form>
<fieldset data-role="controlgroup" data-type="horizontal">
<label>Birds<input type="checkbox" id="choose-birds-regular"></input></label>
<label>Humans<input type="checkbox" id="choose-humans-regular"></input></label>
<label>Fish<input type="checkbox" id="choose-fish-regular"></input></label>
</fieldset>
</form>
</li>
</ul>
</div>
</div>
</body>
</html>
不過~光是這樣是不夠的,必須要以資料庫所記錄的系統樹狀圖去顯示系統導覽才對,一個完整的管理資訊系統通常有上百個功能,這些功能通常是記錄在資料庫,並且由程式碼透過這些資料,產生一個系統導覽樹,方便使用者選擇自己想要的功能。
首先先來介紹系統導覽要記錄在資料庫的資料表,雖然跟之前文章[JQM][008][MVC4+JQM]建立系統導覽目錄(利用可折疊的div)幾乎一樣,但是為了以後方便參考,還是全部再寫一次吧:
fun_id:這個節點的id, 同時也是pk
fun_name:這個節點的中文名稱
parent_id:父節點的pk
url:節點的網址。如果這個節點的種類是目錄,則為空字串;如果這個節點的種類是某某功能(俗稱葉節點),就會是這個功能的超連結網址
sort:這個節點的排列優先順序
active:這個節點是否使用中(Y)或是停用(N)
然後我們新增一些資料到db以供後面作為測試,其實就是做一些會產生巢狀導覽樹(Nested ListView)的資料拉,可以看到根目錄那筆資料的父節點為空字串(這代表樹的第一層),根目錄葉節點001開始那四筆資料的父節點是root(代表樹的第二層),子功能001_1開始的那三筆資料的父節點是rootDir001(代表樹的第三層),理論上要產生幾層的樹都可以拉,但也不要太多層拉,不然畫面過於複雜也不好,容易眼睛拖窗:
INSERT dbo.MenuFunctions VALUES ('root', N'根目錄', '', '', 0, 'Y')
INSERT dbo.MenuFunctions VALUES ('rootfunction001', N'根目錄葉節點001', 'root', 'https://tw.yahoo.com//', 777, 'Y')
INSERT dbo.MenuFunctions VALUES ('rootfunction002', N'根目錄葉節點002', 'root', 'https://tw.yahoo.com//', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('rootDir001', N'根目錄的子目錄001', 'root', '', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('rootDir002', N'根目錄的子目錄002', 'root', '', 2, 'Y')
INSERT dbo.MenuFunctions VALUES ('func001_1', N'子功能001_1', 'rootDir001', 'https://tw.yahoo.com/', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('func001_2', N'子功能001_2', 'rootDir001', 'https://tw.yahoo.com/', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('dir001_1', N'子目錄001_1', 'rootDir001', '', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('dir002_1', N'子目錄002_1', 'rootDir002', '', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('func002_1', N'子功能002_1', 'rootDir002', 'https://tw.yahoo.com/', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('func001_1_1', N'子功能001_1_1', 'dir001_1', 'https://tw.yahoo.com/', 1, 'Y')
INSERT dbo.MenuFunctions VALUES ('func002_1_1', N'子功能002_1_1', 'dir002_1', 'https://tw.yahoo.com/', 1, 'Y')
然後新增一個類別來控制這個遞迴的行為:叫做ListViewTree.cs,先post出完整內容,再就內容的重點作些說明:
using JQueryMobileMVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
namespace JQueryMobileMVC.Libs
{
//要使用此class產生系統導覽樹的話,請用jquery mobile 1.4以上的版本才有喔
public class ListViewTree
{
private AdventureWorksEntities1 ad = new AdventureWorksEntities1();
private string html = "";
/// <summary>
/// 根據一個目錄的id產生一個目錄樹
/// </summary>
/// <param name="funId">傳入根目錄的id</param>
public ListViewTree(string funId)
{
StringBuilder sb = new StringBuilder();
html = GetHtml(funId, true);
}
private string GetHtml(string parentId,bool outerListView = false)
{
StringBuilder sb = new StringBuilder();
var functions = ad.MenuFunctions.Where(s => s.parent_id == parentId).OrderBy(s => s.sort);
if(functions.Count() > 0)
{
if (outerListView)
{
sb.AppendLine("<ul data-role=\"listview\" class=\"ui-listview-outer\" data-inset=\"true\">");
}
else
{
sb.AppendLine("<ul data-role=\"listview\" data-shadow=\"false\" data-inset=\"true\" data-corners=\"false\">");
}
foreach(var f in functions )
{
if(!String.IsNullOrEmpty(f.url ))
{
//leave
sb.AppendLine("<li>" + "<a href=\"" + f.url + "\">" + f.fun_name + "</a>" + "</li>");
}
else
{
//non leave
sb.AppendLine("<li data-role=\"collapsible\" data-iconpos=\"right\" data-shadow=\"false\" data-corners=\"false\">");
sb.AppendLine(" <h2>" + f.fun_name + "</h2>");
string nestedHtml = GetHtml(f.fun_id);
sb.AppendLine(nestedHtml);
sb.AppendLine("</li>");
}
}
sb.AppendLine("</ul>");
}
return sb.ToString();
}
public override string ToString()
{
return html;
}
}
}
以下開始介紹ListViewTree.cs類別的重點處:
系統導覽樹的一切開頭都是從pk為parent_id的資料是否存在開始,存在的話之後會去遞迴的呼叫自己
var functions = ad.MenuFunctions.Where(s => s.parent_id == parentId).OrderBy(s => s.sort);
如果是最外層(即從根目錄開始建構此系統導覽樹的話),class需設定為ui-listview-outer,才符合jquery mobile官網給定的css設定:
if (outerListView)
{
sb.AppendLine("<ul data-role=\"listview\" class=\"ui-listview-outer\" data-inset=\"true\">");
}
else
{
sb.AppendLine("<ul data-role=\"listview\" data-shadow=\"false\" data-inset=\"true\" data-corners=\"false\">");
}
如果碰到葉節點(超連結的節點)就直接輸出<li>,如果碰到目錄的節點,就輸出<li data-role="collapsible".....></li>
foreach(var f in functions )
{
if(!String.IsNullOrEmpty(f.url ))
{
//leave
sb.AppendLine("<li>" + "<a href=\"" + f.url + "\">" + f.fun_name + "</a>" + "</li>");
}
else
{
//non leave
sb.AppendLine("<li data-role=\"collapsible\" data-iconpos=\"right\" data-shadow=\"false\" data-corners=\"false\">");
sb.AppendLine(" <h2>" + f.fun_name + "</h2>");
string nestedHtml = GetHtml(f.fun_id);
sb.AppendLine(nestedHtml);
sb.AppendLine("</li>");
}
}
在Controller中引用這個類別的方式如下,將剛剛類別產生的結果塞到伺服器端的ViewBag變數:
public ActionResult TestNestedListViewByClass()
{
//利用自訂類別產生巢狀ListView
ViewBag.Title = "測試巢狀的ListViewByClass:";
ListViewTree tree = new ListViewTree("");
ViewBag.ListViewTree = tree.ToString();
return PartialView("_TestNestedListViewByClass");
}
並且在新增的view(_TestNestedListViewByClass.cshtml)利用@Html.Raw(ViewBag.ListViewTree)輸出html碼:
<div data-role="page" data-theme="a">
<div data-role="header">
<h1>@ViewBag.Title</h1>
</div>
<div data-role="content">
@Html.Raw(ViewBag.ListViewTree)
</div>
</div>
完整的_TestNestedListViewByClass.cshtml如下,其實重點就是include進去官網建議的.js還有.css還有寫入ui-listview-outer這個類別的css而已:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<style id="collapsible-list-item-style">
/* Basic settings */
.ui-li-static.ui-collapsible {
padding: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview,
.ui-li-static.ui-collapsible > .ui-collapsible-heading {
margin: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content {
padding-top: 0;
padding-bottom: 0;
padding-right: 0;
border-bottom-width: 0;
}
/* collapse vertical borders */
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-last-child,
.ui-li-static.ui-collapsible.ui-collapsible-collapsed > .ui-collapsible-heading > a.ui-btn {
border-bottom-width: 0;
}
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-first-child,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li.ui-first-child > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn {
border-top-width: 0;
}
/* Remove right borders */
.ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > .ui-li-static,
.ui-li-static.ui-collapsible > .ui-collapsible-content > .ui-listview > li > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content {
border-right-width: 0;
}
/* Remove left borders */
/* Here, we need class ui-listview-outer to identify the outermost listview */
.ui-listview-outer > .ui-li-static.ui-collapsible .ui-li-static.ui-collapsible.ui-collapsible,
.ui-listview-outer > .ui-li-static.ui-collapsible > .ui-collapsible-heading > a.ui-btn,
.ui-li-static.ui-collapsible > .ui-collapsible-content {
border-left-width: 0;
}
</style>
<script>
$(document).ready(function () {
$.mobile.ajaxEnabled = false;
});
</script>
</head>
<body>
<div data-role="page" data-theme="a">
<div data-role="header">
<h1>@ViewBag.Title</h1>
</div>
<div data-role="content">
@Html.Raw(ViewBag.ListViewTree)
</div>
</div>
</body>
</html>
執行畫面就可以用巢狀的方式顯示系統導覽樹囉:電腦版+手機版:
這篇大概是這樣,管理資訊系統專用的系統導覽樹大致這樣實做即可。
參考文章:
Indented collapsible list items