[JQM][010][MVC4+JQM]建立系統導覽目錄(利用巢狀的listView)(限定jquery mobile 1.4以上才有喔)

  • 981
  • 0
  • 2015-12-01

建立系統導覽目錄(利用巢狀的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