ASP.NET Core 一步步搭建個人網站(3)_菜單管理

来源:https://www.cnblogs.com/lizzie-xhu/archive/2017/12/28/8136442.html
-Advertisement-
Play Games

上一章,我們實現了用戶的註冊和登錄,登錄之後展示的是我們的主頁,頁面的左側是多級的導航菜單,定位並展示用戶需要訪問的不同頁面。目前導航菜單是寫死的,考慮以後菜單管理的便捷性,我們這節實現下可視化配置菜單的功能,這樣以後我們可以動態的配置導航菜單,不用再編譯發佈網站程式了。 增加後臺管理模塊 第1步, ...


上一章,我們實現了用戶的註冊和登錄,登錄之後展示的是我們的主頁,頁面的左側是多級的導航菜單,定位並展示用戶需要訪問的不同頁面。目前導航菜單是寫死的,考慮以後菜單管理的便捷性,我們這節實現下可視化配置菜單的功能,這樣以後我們可以動態的配置導航菜單,不用再編譯發佈網站程式了。

增加後臺管理模塊

第1步,左側導航菜單中,添加後臺管理模塊,用作管理員登錄後,可以進行一些後臺管理的操作,當然,目前還沒有許可權控制(後期加入),所以對所有用戶可見。大概菜單結構如下:

有了菜單項,我們還需要控制視圖的跳轉,所以,接下來需要寫對應的控制器和視圖。

為了將相關功能組織成一組單獨命名空間(路由)和文件夾結構(視圖),解決方案中右鍵添加區域(Area),取名後臺管理(Configuration),代表後臺管理模塊,.Net Core腳手架(scaffold)自動幫我們實現了目錄劃分:控制器(Controllers)、模型(Models)、視圖(Views)

菜單模型定義

菜單的基本屬性有:菜單名稱、菜單類型、菜單的圖標樣式、菜單url路徑。另外,菜單在邏輯上是樹狀結構,但是要在物理資料庫中存儲,需要進行扁平化處理,每個菜單項有個父菜單屬性(根節點的父菜單為空),還有同一父節點底下,在組類的排序屬性,定義如下:

    /// <summary>
    /// 菜單
    /// </summary>
    public class Menu
    {
        /// <summary>
        /// 主鍵ID
        /// </summary>
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Required(ErrorMessage = "請輸入菜單編號")]
        public string Id { get; set; }

        /// <summary>
        /// 菜單名稱
        /// </summary>
        [Required(ErrorMessage = "請輸入菜單名稱")]
        [StringLength(256)]
        public string Name { get; set; }

        /// <summary>
        /// 父級ID
        /// </summary>
        [DisplayFormat(NullDisplayText = "")]
        public string ParentId { get; set; }

        /// <summary>
        /// 菜單組內排序
        /// </summary>
        [Range(0, 99, ErrorMessage = "請選擇1-99範圍內的整數")]
        public int IndexCode { get; set; }

        /// <summary>
        /// 菜單路徑
        /// </summary>
        [StringLength(256)]
        [DisplayFormat(NullDisplayText = "")]
        public string Url { get; set; }

        /// <summary>
        /// 類型:0導航菜單;1操作按鈕。
        /// </summary>
        [Required(ErrorMessage = "請選擇菜單類型")]
        public MenuTypes? MenuType { get; set; }

        /// <summary>
        /// 菜單圖標名稱
        /// </summary>
        [Required(ErrorMessage = "請輸入菜單圖標")]
        [StringLength(50)]
        public string Icon { get; set; }

        /// <summary>
        /// 菜單備註
        /// </summary>
        public string Remarks { get; set; }
    }
    /// <summary>
    /// 菜單類型
    /// </summary>
    public enum MenuTypes
    {
        /// <summary>
        /// 導航菜單
        /// </summary>
        導航菜單,
        /// <summary>
        /// 操作菜單
        /// </summary>
        操作菜單
    }
View Code

有了我們的菜單模型,在控制器目錄中,我們右鍵建立第1個自己的控制器,取名MenuController,用來菜單管理,上下文選取定義好的Menu模型,還是利用腳手架,自動幫我們生成增刪改查對應的後來邏輯和視圖。此時,我們把菜單導向該控制器,其實是可以正常訪問的,不過還遠遠達不到我們的要求,所以我們還得完善下自動生成的代碼。

菜單控制器改寫

為了方便今後的拓展,新增一個AppController控制器,繼承Controller,以後所有的控制器,都繼承於AppController,方便一些公共的方法調用。

.Net Core有個比較方便的一點,就是實現了構造器的依賴註入,這樣我們不用像以前那樣手工New一個DBContext對象,直接在控制器將需要的DBContext註入,調用的時候,直接訪問註入的對象即可,有關依賴註入的知識,這裡就不在多說了,有興趣大家可以瞭解一下:.Net Core依賴註入

首先,在ApplicationDbContext添加Menu數據集

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }

        public DbSet<ApplicationUser> ApplicationUsers { get; set; }

        public DbSet<Menu> Menus { get; set; }
    }
View Code

這裡我們修改下MenuController構造器:

        private readonly ApplicationDbContext _context;

        public MenuController(ApplicationDbContext context, INavMenuService navMenuService)
        {
            _context = context;
            _NavMenuService = navMenuService;
        }
View Code

為了後面方便統一提供下拉框選擇,這裡實現一個下拉框初始化方法:

        /// <summary>
        /// 初始化下拉選擇框
        /// </summary>
        /// <param name="menu"></param>
        private void UpdateDropDownList(Menu menu = null)
        {
            var menusParent = _context.Menus.AsNoTracking().Where(s => s.MenuType == MenuTypes.導航菜單);
            List<SelectListItem> listMenusParent = new List<SelectListItem>();
            foreach (var menuParent in menusParent)
            {
                listMenusParent.Add(new SelectListItem
                {
                    Value = menuParent.Id,
                    Text = menuParent.Id + $"({menuParent.Name})",
                    Selected = (menu != null && menuParent.Id == menu.ParentId)
                });
            }
            ViewBag.ParentIds = listMenusParent;

            if (menu == null)
            {
                ViewBag.MenuTypes = MenuTypes.導航菜單.GetSelectListByEnum();
            }
            else
            {
                ViewBag.MenuTypes = MenuTypes.導航菜單.GetSelectListByEnum(Convert.ToInt32(menu.MenuType));
            }
        }
View Code

列表頁改寫

控制器調整:增加查詢傳入參數,根據參數篩選查詢結果;

        /// <summary>
        /// 列表頁
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public async Task<IActionResult> Index(MenuIndexQuery query)
        {
            var menus = _context.Menus.AsNoTracking();
            if (!string.IsNullOrEmpty(query.QName))
            {
                menus= menus.Where(s => s.Name.Contains(query.QName.Trim()));
            }
            if (!string.IsNullOrEmpty(query.QId))
            {
                menus = menus.Where(s => s.Id.Contains(query.QId.Trim()));
            }
            if (!string.IsNullOrEmpty(query.QParentId))
            {
                menus = menus.Where(s => s.ParentId == query.QParentId.Trim());
            }
            if (query.QMenuType != null)
            {
                menus = menus.Where(s => s.MenuType == query.QMenuType);
            }

            UpdateDropDownList();
            return View(new MenuIndexVM { Menus = await menus.ToListAsync(), Query = query });
        }
View Code

視圖調整:用戶點擊刪除時,彈出確認框,調用Ajax方式刪除數據,不再通過頁面跳轉;

@using MyWebSite.ViewModels
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@model MyWebSite.Areas.Configuration.ViewModels.MenuIndexVM
@{
    ViewData["Title"] = "菜單列表";

    var breadcrumb = new BreadCrumb("菜單列表", "Version 2.0", new List<NavCrumb>
{
new NavCrumb(name:"菜單管理",url: "/Configuration/Menu"),
new NavCrumb(name:"菜單列表"),
});
    ViewBag.BreadCrumb = breadcrumb;

    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="row">
    <div class="col-xs-12">
        <div class="box with-border">
            <form class="form" asp-action="Index">
                <div class="box-header">
                    <h3 class="box-title"><i class="fa fa-search margin-r-5">查詢條件</i></h3>
                    <div class="box-tools pull-right">
                        <button type="submit" class="btn btn-success margin-r-5"><i class="fa fa-search margin-r-5"></i>查詢</button>
                        <a class="btn btn-primary" href="/Configuration/Menu/Create"><i class="fa fa-plus margin-r-5"></i>新建</a>
                    </div>
                    <div asp-validation-summary="All" class="text-danger"></div>
                    <div class="row">
                        <div class="col-md-3">
                            <div class="form-group">
                                <label asp-for="Query.QName">菜單名稱:</label>
                                <input asp-for="Query.QName" class="form-control input-sm">
                            </div>
                        </div>
                        <div class="col-md-3">
                            <div class="form-group">
                                <label asp-for="Query.QId">菜單編碼:</label>
                                <input asp-for="Query.QId" class="form-control input-sm">
                            </div>
                        </div>
                        <div class="col-md-3">
                            <div class="form-group">
                                <label asp-for="Query.QParentId">父級菜單:</label>
                                <select asp-for="Query.QParentId" class="form-control input-sm select2" asp-items="ViewBag.ParentIds">
                                    <option value="">-- 請選擇 --</option>
                                </select>
                            </div>
                        </div>
                        <div class="col-md-3">
                            <div class="form-group">
                                <label asp-for="Query.QMenuType">菜單類型:</label>
                                <select asp-for="Query.QMenuType" class="form-control input-sm select2" asp-items="ViewBag.MenuTypes">
                                    <option value="">-- 請選擇 --</option>
                                </select>
                            </div>
                        </div>
                    </div>
                </div>
            </form>
            <div class="box-body">
                <table class="table table-bordered table-hover" style="width: 100%">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>菜單名稱</th>
                            <th>菜單編號</th>
                            <th>父級編號</th>
                            <th>組內排序</th>
                            <th>菜單類型</th>
                            <th>菜單圖標</th>
                            <th>菜單路徑</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        @{
                            var index = 0;
                        }
                        @foreach (var item in Model.Menus)
                        {
                            index++;
                            <tr>
                                <td>
                                    @index.ToString("D3")
                                </td>
                                <td>
                                    @Html.ActionLink(@item.Name, "Details", new { id = @item.Id })
                                </td>
                                <td>
                                    <span>@item.Id</span>
                                </td>
                                <td>
                                    <span>@Html.DisplayFor(modelItem => item.ParentId)</span>
                                </td>
                                <td>
                                    <span>@item.IndexCode</span>
                                </td>
                                <td>
                                    <span>@item.MenuType</span>
                                </td>
                                <td>
                                    <i class="fa @item.Icon" data-toggle="tooltip" data-placement="right" title="@item.Icon"></i>
                                </td>
                                <td>
                                    <i class="fa fa-ellipsis-h" data-toggle="tooltip" data-placement="top" title="@Html.DisplayFor(modelItem => item.Url)"></i>
                                </td>
                                <td>
                                    @Html.ActionLink("編輯", "Edit", new { id = @item.Id })|
                                    @Html.ActionLink("詳情", "Details", new { id = @item.Id })|
                                    <a href="#" onclick="onDelete('@item.Id', '@item.Name');">刪除</a>
                                </td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

@section Scripts{
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script>
        function onDelete(id, name) {
            BootstrapDialog.show({
                message: '確認刪除菜單-' + name + '[' + id + ']?',
                size: BootstrapDialog.SIZE_SMALL,
                draggable: true,
                buttons: [
                    {
                        icon: 'fa fa-check',
                        label: '確定',
                        cssClass: 'btn-primary',
                        action: function (dialogRef) {
                            dialogRef.close();
                            $.ajax({
                                type: 'POST',
                                url: '/Configuration/Menu/Delete',
                                data: { id: id },
                                success: function () {
                                    location.reload();
                                }
                            });
                        }
                    }, {
                        icon: 'fa fa-close',
                        label: '取消',
                        action: function (dialogRef) {
                            dialogRef.close();
                        }
                    }
                ]
            });
        }
    </script>
}
View Code

新建頁改寫

 控制器調整:這裡控制器有2個Create方法,一個是Http Get類型,用戶列表頁點新建時,跳轉到該方法,另外一個是Http Post類型,用戶填完新建的菜單信息後,點擊保存,跳轉到該方法。在Http Post方法中,為了防止頁面over post,需要指定綁定的屬性Bind("Id,Name,ParentId,IndexCode,Url,MenuType,Icon,Remarks"),當然,也可以用TryUpdateModel()實現,以後再介紹;

        /// <summary>
        /// 新建空白頁面
        /// </summary>
        /// <returns></returns>
        public IActionResult Create()
        {
            var model = new Menu
            {
                Id = "MXX_XX_XX",
                IndexCode = 1,
                Icon = "fa-circle-o"
            };
            UpdateDropDownList();
            return View(model);
        }

        /// <summary>
        /// 新建保存頁面
        /// </summary>
        /// <param name="menu"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> Create([Bind("Id,Name,ParentId,IndexCode,Url,MenuType,Icon,Remarks")] Menu menu)
        {
            if (ModelState.IsValid)
            {
                if (!MenuExists(menu.Id))
                {
                    _context.Add(menu);
                    await _context.SaveChangesAsync();

                    _NavMenuService.InitOrUpdate();
                    return RedirectToAction(nameof(Index));
                }
                else
                {
                    ModelState.AddModelError("Id", "菜單編號已存在,請修改菜單編號.");
                }
            }
            UpdateDropDownList(menu);
            return View(menu);
        }
View Code

視圖調整: 引入前端數據驗證,並增加一些數據控制,比如菜單類型非操作菜單時,菜單路徑不可編輯等等;

@using MyWebSite.ViewModels
@using MyWebSite.Areas.Configuration.Models
@model MyWebSite.Areas.Configuration.Models.Menu

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
    ViewData["Title"] = "菜單新建";

    var breadcrumb = new BreadCrumb("菜單新建", "Version 2.0", new List<NavCrumb>
    {
        new NavCrumb(name:"菜單管理",url: "/Configuration/Menu"),
        new NavCrumb(name:"菜單新建"),
    });
    ViewBag.BreadCrumb = breadcrumb;

    Layout = "~/Views/Shared/_Layout.cshtml";
}
<section class="content">
    <div class="row">
        <div class="col-md-8">
            <div class="box">
                <div class="box-header with-border">
                    <h3 class="box-title">新建</h3>
                </div>
                <form asp-action="Create">
                    <div asp-validation-summary="All" class="text-danger"></div>
                    <div class="box-body">
                        <div class="form-group col-md-6">
                            <label asp-for="Id">菜單編號</label>
                            <input asp-for="Id" class="form-control input-sm">
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="Name">菜單名稱</label>
                            <input asp-for="Name" class="form-control input-sm">
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="ParentId">父級菜單</label>
                            <select asp-for="ParentId" class="form-control input-sm select2" asp-items="ViewBag.ParentIds">
                                <option value="">-- 請選擇 --</option>
                            </select>
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="IndexCode">組內排序</label>
                            <input asp-for="IndexCode" class="form-control input-sm">
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="MenuType">菜單類型</label>
                            <select asp-for="MenuType" class="form-control input-sm" asp-items="ViewBag.MenuTypes">
                                <option value="">-- 請選擇 --</option>
                            </select>
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="Icon">菜單圖標</label>
                            <div class="input-group">
                                <span class="input-group-addon"><i id="IconfShow" class="fa @Model.Icon"></i></span>
                                <input asp-for="Icon" class="form-control input-sm">
                            </div>
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="Url">菜單路徑</label>
                            @if (Model.MenuType == MenuTypes.操作菜單)
                            {
                                <input asp-for="Url" class="form-control input-sm">
                            }
                            else
                            {
                                <input asp-for="Url" class="form-control input-sm" readonly>
                            }
                        </div>
                        <div class="form-group  col-md-6">
                            <label asp-for="Remarks">備註</label>
                            <input asp-for="Remarks" class="form-control input-sm">
                        </div>
                    </div>
                    <div class="box-footer">
                        <button type="submit" class="btn btn-primary"><i id="IconfShow" class="fa fa-save"></i>&nbsp;保存</button>
                        <a asp-action="Index" class="btn btn-default"><i id="IconfShow" class="fa fa-undo"></i>&nbsp;返回</a>
                    </div>
                </form>
            </div>

        </div>
    </div>
</section>
@section Scripts {
    <script src="~/js/Configuration/Menu.js"></script>
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
View Code

詳情頁改寫

 控制器調整:不用大的調整,只是增加了下拉框的初始化工作 ;

        /// <summary>
        /// 詳情頁
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<IActionResult> Details(string id)
        {
            if (id == null)
            {
                return	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 可以把字元串轉換為MemoryStream。也可以把MenoryStream轉換為字元串。 下麵Insus.NET寫了幾個方法: class Bq { public string Input { get; set; } public byte[] Byte { get; set; } public ...
  • 知識點目錄 >傳送門 首先推薦兩篇大牛寫的委托和事件的博客,寫的超級好!看了就包你看會,想學習的朋友直接看這兩篇就足以,我自己寫的是算是自己學習的紀錄。 傳送門 》C# 中的委托和事件 C# 中的委托和事件續。 委托是什麼? 委托是一個類,它定義了一種的類型,使得可以將方法當作另一個方法的參數來進行 ...
  • 在控制台應用程式中,獲取電腦名: class Bp { public void LocalComputerName() { var computerName = System.Environment.MachineName; Console.WriteLine(computerName); Con ...
  • 這段時間,Unity項目遇到問題,工程中有很多有問題的代碼,調試的時候,這些代碼理應拋出異常但是卻沒有,這是一個很恐怖的事情,因為你根本不知道你代碼中有哪些不對,程式運行下來,就會遇到各種邏輯問題,查無對策。後來我研究了下,記錄了下來 在unity中,子線程下的異常,如果不加try catch,可能 ...
  • 當你需要對某一字元或字元串重覆輸出時,可以參考下麵2個方法。一個是new 字元串,另一個是使用Linq的Enumberable的Repeat方法來實現。 class Bo { public void RepeatCharacter(char c, int times) { string output ...
  • 回到目錄 當我們進行軟體開發時,一般會寫單元測試,而對於業務情景來說,一般是測試它的業務邏輯準確性,對於你的測試數據是否來自資料庫還是文件,是否為真實還是模擬,並不是很關心!我關心的就是我的業務邏輯是否正確! 所以我們的單元測試在調用底層介面時,尤其是數據持久層的介面時,一般可以使用mock的方式, ...
  • 最近在學python學了簡單的從網上抓取圖片:剛好做一個C#版本的: 下麵貼代碼: using System;using System.IO;using System.Collections.Generic;using static System.Console;using System.Text; ...
  • 比如,某一個陣列中,有重覆的元素,我們想去除重覆的,保留一個。HashSet<T>含不重覆項的無序列表,從MSDN網上瞭解到,這集合基於散列值,插入元素的操作非常快。你可以寫一個方法: class Bn { public string[] Data { get; set; } public stri ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...