基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(五)
系列文章
- 基于 abp vNext 和 .NET Core 开发博客项目 – 使用 abp cli 搭建项目
- 基于 abp vNext 和 .NET Core 开发博客项目 – 给项目瘦身,让它跑起来
- 基于 abp vNext 和 .NET Core 开发博客项目 – 完善与美化,Swagger登场
- 基于 abp vNext 和 .NET Core 开发博客项目 – 数据访问和代码优先
- 基于 abp vNext 和 .NET Core 开发博客项目 – 自定义仓储之增删改查
- 基于 abp vNext 和 .NET Core 开发博客项目 – 统一规范API,包装返回模型
- 基于 abp vNext 和 .NET Core 开发博客项目 – 再说Swagger,分组、描述、小绿锁
- 基于 abp vNext 和 .NET Core 开发博客项目 – 接入GitHub,用JWT保护你的API
- 基于 abp vNext 和 .NET Core 开发博客项目 – 异常处理和日志记录
- 基于 abp vNext 和 .NET Core 开发博客项目 – 使用Redis缓存数据
- 基于 abp vNext 和 .NET Core 开发博客项目 – 集成Hangfire实现定时任务处理
- 基于 abp vNext 和 .NET Core 开发博客项目 – 用AutoMapper搞定对象映射
- 基于 abp vNext 和 .NET Core 开发博客项目 – 定时任务最佳实战(一)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 定时任务最佳实战(二)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 定时任务最佳实战(三)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 博客接口实战篇(一)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 博客接口实战篇(二)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 博客接口实战篇(三)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 博客接口实战篇(四)
- 基于 abp vNext 和 .NET Core 开发博客项目 – 博客接口实战篇(五)
- 基于 abp vNext 和 .NET Core 开发博客项目 – Blazor 实战系列(一)
- 基于 abp vNext 和 .NET Core 开发博客项目 – Blazor 实战系列(二)
- 基于 abp vNext 和 .NET Core 开发博客项目 – Blazor 实战系列(三)
- 基于 abp vNext 和 .NET Core 开发博客项目 – Blazor 实战系列(四)
上一篇完成了分类标签友链的列表查询页面数据绑定,还剩下一个文章详情页的数据没有绑,现在简单的解决掉。
文章详情
之前已经添加了四个参数:year、month、day、name,用来组成我们最终的URL,继续添加一个参数用来接收API返回的数据。
[Parameter]
public int year { get; set; }
[Parameter]
public int month { get; set; }
[Parameter]
public int day { get; set; }
[Parameter]
public string name { get; set; }
/// <summary>
/// URL
/// </summary>
private string url => $"/{year}/{(month >= 10 ? month.ToString() : $"0{month}")}/{(day >= 10 ? day.ToString() : $"0{day}")}/{name}/";
/// <summary>
/// 文章详情数据
/// </summary>
private ServiceResult<PostDetailDto> post;
然后在初始化方法OnInitializedAsync()
中请求数据。
/// <summary>
/// 初始化
/// </summary>
protected override async Task OnInitializedAsync()
{
// 获取数据
post = await Http.GetFromJsonAsync<ServiceResult<PostDetailDto>>($"/blog/post?url={url}");
}
现在拿到了post数据,然后在HTML中绑定即可。
@if (post == null)
{
<Loading />
}
else
{
@if (post.Success)
{
var _post = post.Result;
<article class="post-wrap">
<header class="post-header">
<h1 class="post-title">@_post.Title</h1>
<div class="post-meta">
Author: <a itemprop="author" rel="author" href="javascript:;">@_post.Author</a>
<span class="post-time">
Date: <a href="javascript:;">@_post.CreationTime</a>
</span>
<span class="post-category">
Category:<a href="/category/@_post.Category.DisplayName/">@_post.Category.CategoryName</a>
</span>
</div>
</header>
<div class="post-content" id="content">
@((MarkupString)_post.Html)
</div>
<section class="post-copyright">
<p class="copyright-item">
<span>Author:</span>
<span>@_post.Author</span>
</p>
<p class="copyright-item">
<span>Permalink:</span>
<span><a href="/post@_post.Url">https://meowv.com/post@_post.Url</a></span>
</p>
<p class="copyright-item">
<span>License:</span>
<span>本文采用<a target="_blank" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"> 知识共享 署名-非商业性使用-禁止演绎(CC BY-NC-ND)国际许可协议 </a>进行许可</span>
</p>
</section>
<section class="post-tags">
<div>
<span>Tag(s):</span>
<span class="tag">
@if (_post.Tags.Any())
{
@foreach (var tag in _post.Tags)
{
<a href="/tag/@tag.DisplayName/"># @tag.TagName</a>
}
}
</span>
</div>
<div>
<a @onclick="async () => await Common.BaskAsync()">back</a>
<span>· </span>
<a href="/">home</a>
</div>
</section>
<section class="post-nav">
@if (_post.Previous != null)
{
<a class="prev"
rel="prev"
@onclick="@(async () => await Common.NavigateTo($"/post{_post.Previous.Url}, true))"
href="/post@_post.Previous.Url">@_post.Previous.Title</a>
}
@if (_post.Next != null)
{
<a class="next"
rel="next"
@onclick="@(async () => await Common.NavigateTo($"/post{_post.Next.Url}", true))"
href="/post@_post.Next.Url">
@_post.Next.Title
</a>
}
</section>
</article>
}
else
{
<ErrorTip />
}
}
其中有几个地方需要注意一下:
我们从post对象中取到的文章内容HTML,直接显示是不行了,需要将其解析为HTML标签,需要用到MarkupString
。
然后页面上有一个后退按钮,这里我在Common.cs
中写了一个方法来实现。
/// <summary>
/// 后退
/// </summary>
/// <returns></returns>
public async Task BaskAsync()
{
await InvokeAsync("window.history.back");
}
还有就是上一篇和下一篇的问题,将具体的URL传递给NavigateTo()
方法,然后跳转过去即可。
在Common.cs
中将之前文章创建RenderPage()
方法修改成NavigateTo()
。这个命名更好一点。
/// <summary>
/// 跳转指定URL
/// </summary>
/// <param name="uri"></param>
/// <param name="forceLoad">true,绕过路由刷新页面</param>
/// <returns></returns>
public async Task NavigateTo(string url, bool forceLoad = false)
{
_navigationManager.NavigateTo(url, forceLoad);
await Task.CompletedTask;
}
现在数据算是绑定完了,但是遇到了一个大问题,就是详情页面的样式问题,因为用到了Markdown,所以之前是加载了许多JS文件来处理的。那么现在肯定行不通了,所以关于详情页的样式问题暂时搁浅,让我寻找一下好多解决方式。
现在显示是没有问题了,就是不太好看,还有关于添加文章的功能,不知道有什么好的 Markdown 编辑器可以推荐我使用。
到这里Blazor的前端展示页面已经全部弄完了,接下来开始写后台相关的页面。
后台首页
关于后台管理的所有页面都放在Admin文件夹下,在Pages文件夹下新建Admin文件夹,然后先添加两个组件页面:Admin.razor
、Auth.razor
。
Admin.razor
为后台管理的首页入口,我们在里面直接添加几个预知的链接并设置其路由。
@page "/admin"
<div class="post-wrap">
<h2 class="post-title">- 博客内容管理 -</h2>
<ul>
<li>
<a href="/admin/post"><h3>