1

从零开始Blazor Server(9)--修改Layout - jvx

 1 year ago
source link: https://www.cnblogs.com/j4587698/p/16565185.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

目前我们的MainLayout还是默认的,这里我们需要修改为BootstrapBlazor的Layout,并且处理一下菜单。

修改MainLayout

BootstrapBlazor已经自带了一个Layout组件,这个组件里常用功能已经很全了,所以我们直接使用这个组件即可。

<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true" IsFixedFooter="true"
        ShowFooter="true" ShowCollapseBar="true" OnCollapsed="@OnCollapsed" Menus="@_menuItems">
    <Header>
            <span class="ms-3 flex-sm-fill d-none d-sm-block">BlazorLearn</span>
        <div class="flex-fill d-sm-none">
        </div>
        <Logout ImageUrl="images/argo-c.png" DisplayName="@_user.Name" UserName="@_user.UserName">
                    <LinkTemplate>
                        <LogoutLink Url="/api/account/logout"></LogoutLink>
                    </LinkTemplate>
                </Logout>
    </Header>
    <Side>
            <div class="layout-banner">
                <img class="layout-logo" src="images/Argo.png" />
                <div class="layout-title">
                    <span>BlazorLearn</span>
                </div>
            </div>
        </Side>
    <Main>
        <CascadingValue Value="this" IsFixed="true">
            @Body
        </CascadingValue>
    </Main>
    <Footer>
        <div class="text-center flex-fill">
            <a href="/" target="_blank">BlazorLearn</a>
        </div>
    </Footer>
</Layout>

@code
{
    private bool IsCollapsed { get; set; }
    
    private List<MenuItem>? _menuItems;

    [NotNull]
    private UserEntity? _user;
    
    private Task OnCollapsed(bool collapsed)
    {
        IsCollapsed = collapsed;
        return Task.CompletedTask;
    }
    
    protected override void OnInitialized()
    {
        base.OnInitialized();
        _user = UserEntity.Where(x => x.UserName == Furion.App.User.FindFirstValue(ClaimTypes.Name)).First();
        if (_user == null)
        {
            return;
        }
        _menuItems = CreateMenuItems(MenuEntity.Where(x => x.Roles!.Any(y => y.Id == _user.RoleId)).ToList(), 0);
    }

    private List<MenuItem> CreateMenuItems(List<MenuEntity> menus, int parentId)
    {
        var selectedMenus = new List<MenuItem>();
        var selectedMenuEntities = menus.Where(x => x.ParentId == parentId).ToList();

        foreach (var menuEntity in selectedMenuEntities)
        {
            var menuItem = new MenuItem(menuEntity.Name!, menuEntity.Url, menuEntity.Icon);
            menuItem.Items = CreateMenuItems(menus, menuEntity.Id);
            selectedMenus.Add(menuItem);
        }
        return selectedMenus;
    }
}

这里没什么需要多说的,每个参数的意义在文档里都比较清楚,如果需要查询具体的含义,可以看这里

这里需要注意的是,Menus里面必须要定义一个变量,不能直接放一个方法,否则此方法每次跳转都会执行,导致菜单不正常。

另外Logout是一个独立的组件,这个组件其实叫Logout并不贴切,它是一个带有头像,欢迎信息以及下拉菜单的用户信息组件。

这里我们只放一个LogoutLink登出菜单。

修改AdminHandler

如果你直接启动项目,会发现Layout不见了,因为我们的Layout里面做了比较多的处理,会有一个需要权限验证的请求。这个请求不会携带Resource,所以不会返回true。就导致Layout一直不显示,所以我们需要处理这种情况,我们目前修改为Resource里不是RouteData的全部都通过验证。

    public override Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
    {
        if (!int.TryParse(context.User.FindFirst(ClaimTypes.Role)?.Value, out var roleId))
        {
            return Task.FromResult(false);
        }
        if (context.Resource is RouteData routeData)
        {
            var routeAttr = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
                x.AttributeType == typeof(RouteAttribute));
            if (routeAttr == null)
            {
                return Task.FromResult(true);
            }
            else
            {
                var url = routeAttr.ConstructorArguments[0].Value as string;
                var permission = MenuEntity
                    .Where(x => x.Roles!.Any(y => y.Id == roleId) && x.Url == url).First();
                if (permission != null)
                {
                    return Task.FromResult(true);
                }
            }
        }
        else
        {
            return Task.FromResult(true);
        }
        
        return Task.FromResult(false);
    }

这样我们再启动应该就可以看到Layout了。

修改LoginController

我们之前只有一个登录,所以我们写了一个LoginController,现在我们需要加入登出,所以我们直接把LoginController改为AccountController,然后内容改为PostLoginGetLogout

public class AccountController: IDynamicApiController
{
    public async Task<object> PostLogin([FromBody]LoginVo loginVo)
    {
        if (string.IsNullOrEmpty(loginVo.UserName))
        {
            return new { code = 50000, message = "用户名不能为空" };
        }
        if (string.IsNullOrEmpty(loginVo.Password))
        {
            return new { code = 50000, message = "密码不能为空" };
        }

        var password = MD5Encryption.Encrypt(loginVo.Password);
        var user = await UserEntity.Where(x =>
            x.UserName == loginVo.UserName && x.Password == password).Include(x => x.Role).FirstAsync();
        if (user != null)
        {
            var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
            identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName!));
            identity.AddClaim(new Claim(ClaimTypes.Role, user.Role!.Id.ToString()));
            await Furion.App.HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties(){IsPersistent = true, ExpiresUtc = loginVo.RememberMe? DateTimeOffset.Now.AddDays(5): DateTimeOffset.Now.AddMinutes(30)});

            return new { code = 20000, message = "登录成功" };
        }
        return new { code = 50000, message = "用户名或密码错误" };
    }

    [Authorize]
    public async Task<IActionResult> GetLogout()
    {
        await Furion.App.HttpContext.SignOutAsync();
        return new RedirectResult("/Login");
    }
}

这里我们直接给Logout[Authorize],只有登录以后才能访问。

代码在github:https://github.com/j4587698/BlazorLearnj,分支lesson9。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK