系列文章 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場 ...
系列文章
- 基於 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 實戰系列(四)
- 基於 abp vNext 和 .NET Core 開發博客項目 - Blazor 實戰系列(五)
- 基於 abp vNext 和 .NET Core 開發博客項目 - Blazor 實戰系列(六)
- 基於 abp vNext 和 .NET Core 開發博客項目 - Blazor 實戰系列(七)
- 基於 abp vNext 和 .NET Core 開發博客項目 - Blazor 實戰系列(八)
終於要接近尾聲了,上一篇基本上將文章模塊的所有功能都完成了,整個博客頁面也都完成了,本篇主要來美化幾個地方,修修補補。
編輯器主題切換
當我們新增和編輯文章的時候,預設編輯器是白色的,如果點擊了頭部切換主題按鈕,我想要把編輯器主題顏色也做相應的改變該如何去實現呢?
剛好,editor.md
是支持主題切換的,這就比較舒服了,直接按照要求調用對應的方法即可。
在app.js
的renderEditor
函數中我們已經自定義了三個參數theme
、editorTheme
、previewTheme
,這三個參數就是來改變編輯器主題顏色的。
還是將值存在localStorage中,和我們博客的主題切換一樣,這裡叫editorTheme
。
theme: localStorage.editorTheme || 'default',
editorTheme: localStorage.editorTheme === 'dark' ? 'pastel-on-dark' : 'default',
previewTheme: localStorage.editorTheme || 'default',
預設從localStorage
中取數據,如果沒取到的話,給對應的預設值。第二個參數有點特殊,用了一個三元表達式給不同的值。
然後在主題切換的時候也對編輯器做相應的調整即可。
打開Header.razor
頭部組件,找到SwitchTheme()
切換主題的方法,添加一句await Common.SwitchEditorTheme(currentTheme);
。
/// <summary>
/// 切換主題
/// </summary>
private async Task SwitchTheme()
{
currentTheme = currentTheme == "Light" ? "Dark" : "Light";
await Common.SetStorageAsync("theme", currentTheme);
await Common.InvokeAsync("window.func.switchTheme");
var uri = await Common.CurrentUri();
if (uri.AbsolutePath.StartsWith("/admin/post"))
{
await Common.SwitchEditorTheme(currentTheme);
}
}
將具體切換邏輯放到SwitchEditorTheme
中,他接收一個參數currentTheme
,用來判斷是切換黑的還是白的。
/// <summary>
/// 切換編輯器主題
/// </summary>
/// <param name="currentTheme"></param>
/// <returns></returns>
public async Task SwitchEditorTheme(string currentTheme)
{
var editorTheme = currentTheme == "Light" ? "default" : "dark";
await SetStorageAsync("editorTheme", editorTheme);
await InvokeAsync("window.func.switchEditorTheme");
}
切換主題之前拿到當前URI對象,判斷當前請求的鏈接是否是新增和更新文章的那個頁面,即"/admin/post",才去執行切換編輯器主題的方法,當不是這個頁面的時候,編輯器是沒有渲染出來的,如果也執行這段代碼就會報錯。
去看看效果。
文章詳情頁美化
現在的文章詳情頁是沒有將markdown格式渲染出來的,這裡還是使用editor.md
提供的方法,因為需要載入幾個js文件,然後才能渲染樣式。
所以還是在app.js
添加一段代碼。
renderMarkdown: async function () {
await this._loadScript('./editor.md/lib/zepto.min.js').then(function () {
func._loadScript('./editor.md/lib/marked.min.js').then(function () {
func._loadScript('./editor.md/lib/prettify.min.js').then(function () {
func._loadScript('./editor.md/editormd.js').then(function () {
editormd.markdownToHTML("content");
});
});
});
});
},
然後在文章詳情頁的組件Post.razor
中修改代碼,當數據載入完成後調用renderMarkdown
即可,然後還需要引用一個css文件editormd.preview.css
。
提供一下Post.razor
最終的代碼。
@page "/post/{year:int}/{month:int}/{day:int}/{name}"
<link href="./editor.md/css/editormd.preview.css" rel="stylesheet" />
@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.NavigateTo("/posts"))">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 FetchData(_post.Previous.Url))"
href="/post@_post.Previous.Url">@_post.Previous.Title</a>
}
@if (_post.Next != null)
{
<a class="next"
rel="next"
@onclick="@(async () => await FetchData(_post.Next.Url))"
href="/post@_post.Next.Url">
@_post.Next.Title
</a>
}
</section>
</article>
}
else
{
<ErrorTip />
}
}
@code {
[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;
/// <summary>
/// 初始化
/// </summary>
protected override async Task OnInitializedAsync()
{
await FetchData(url);
}
/// <summary>
/// 請求數據,渲染頁面
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private async Task FetchData(string url, bool isPostNav = false)
{
post = await Http.GetFromJsonAsync<ServiceResult<PostDetailDto>>($"/blog/post?url={url}");
await Common.InvokeAsync("window.func.renderMarkdown");
}
}
到這裡整個開發工作便結束了,這裡只是一個小小的實戰系列記錄,沒有深層次的剖析研究Blazor的所有使用方式。
如果本系列對你有些許幫助,便是我最大的收穫,歡迎大家關註我的公眾號:阿星Plus。
開源地址:https://github.com/Meowv/Blog