系列文章
- 基於 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 實戰系列(八)
- 基於 abp vNext 和 .NET Core 開發博客項目 - Blazor 實戰系列(九)
- 基於 abp vNext 和 .NET Core 開發博客項目 - 終結篇之發布項目
上一篇完成了后台分類模塊的所有功能,本篇繼續將標簽模塊和友情鏈接模塊的增刪改查完成。
標簽管理
實現方式和之前的分類管理是一樣的,在Admin文件夾下面添加Tags.razor
組件,設置路由@page "/admin/tags"
。
同樣的內容也需要放在AdminLayout
組件下面,添加幾個參數:彈窗狀態bool Open
、新增或更新時標簽字段string tagName, displayName
、更新時的標簽Idint id
、API返回的標簽列表接收參數ServiceResult<IEnumerable<QueryTagForAdminDto>> tags
。
/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;
/// <summary>
/// 新增或者更新時候的標簽字段值
/// </summary>
private string tagName, displayName;
/// <summary>
/// 更新標簽的Id值
/// </summary>
private int id;
/// <summary>
/// API返回的標簽列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryTagForAdminDto>> tags;
//QueryTagForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class QueryTagForAdminDto : QueryTagDto
{
/// <summary>
/// 主鍵
/// </summary>
public int Id { get; set; }
}
}
在初始化方法OnInitializedAsync()
中獲取數據。
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
var token = await Common.GetStorageAsync("token");
Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
tags = await FetchData();
}
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> FetchData()
{
return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryTagForAdminDto>>>("/blog/admin/tags");
}
注意需要設置請求頭,進行授權訪問,然后頁面上綁定數據。
<AdminLayout>
@if (tags == null)
{
<Loading />
}
else
{
<div class="post-wrap tags">
<h2 class="post-title">- Tags -</h2>
@if (tags.Success && tags.Result.Any())
{
<div class="categories-card">
@foreach (var item in tags.Result)
{
<div class="card-item">
<div class="categories">
<NavLink title="❌刪除" @onclick="@(async () => await DeleteAsync(item.Id))">❌</NavLink>
<NavLink title="📝編輯" @onclick="@(() => ShowBox(item))">📝</NavLink>
<NavLink target="_blank" href="@($"/tag/{item.DisplayName}")">
<h3>@item.TagName</h3>
<small>(@item.Count)</small>
</NavLink>
</div>
</div>
}
<div class="card-item">
<div class="categories">
<NavLink><h3 @onclick="@(() => ShowBox())">📘~~~ 新增標簽 ~~~📘</h3></NavLink>
</div>
</div>
</div>
}
else
{
<ErrorTip />
}
</div>
<Box OnClickCallback="@SubmitAsync" Open="@Open">
<div class="box-item">
<b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" />
</div>
<div class="box-item">
<b>TagName:</b><input type="text" @bind="@tagName" @bind:event="oninput" />
</div>
</Box>
}
</AdminLayout>
tags
沒獲取到數據的時候顯示<Loading />
組件內容,循環遍歷數據進行綁定,刪除按鈕綁定點擊事件調用DeleteAsync()
方法。新增和編輯按鈕點擊事件調用ShowBox()
方法顯示彈窗。新增的時候不需要傳遞參數,編輯的時候需要將當前item即QueryTagForAdminDto
傳遞進去。
<Box>
組件中綁定了標簽的兩個參數,是否打開參數Opne
和確認按鈕回調事件方法SubmitAsync()
。
刪除標簽的方法DeleteAsync(...)
如下:
// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n💥💢真的要干掉這個該死的標簽嗎💢💥");
if (confirmed)
{
var response = await Http.DeleteAsync($"/blog/tag?id={id}");
var result = await response.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
tags = await FetchData();
}
}
刪除之前進行二次確認,避免誤傷,刪除成功重新加載一遍數據。
彈窗的方法ShowBox(...)
如下:
/// <summary>
/// 顯示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryTagForAdminDto dto = null)
{
Open = true;
id = 0;
// 新增
if (dto == null)
{
displayName = null;
tagName = null;
}
else // 更新
{
id = dto.Id;
displayName = dto.DisplayName;
tagName = dto.TagName;
}
}
最后在彈窗中確認按鈕的回調事件方法SubmitAsync()
如下:
/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
var input = new EditTagInput()
{
DisplayName = displayName.Trim(),
TagName = tagName.Trim()
};
if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.TagName))
{
return;
}
var responseMessage = new HttpResponseMessage();
if (id > 0)
responseMessage = await Http.PutAsJsonAsync($"/blog/tag?id={id}", input);
else
responseMessage = await Http.PostAsJsonAsync("/blog/tag", input);
var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
tags = await FetchData();
Open = false;
}
}
輸入參數EditTagInput
。
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class EditTagInput : TagDto
{
}
}
最終執行新增或者更新數據都在點擊事件中進行,將變量的值賦值給EditTagInput
,根據id判斷走新增還是更新,成功后重新加載數據,關掉彈窗。
標簽管理頁面全部代碼如下:
點擊查看代碼
@page "/admin/categories"
<AdminLayout>
@if (categories == null)
{
<Loading />
}
else
{
<div class="post-wrap categories">
<h2 class="post-title">- Categories -</h2>
@if (categories.Success && categories.Result.Any())
{
<div class="categories-card">
@foreach (var item in categories.Result)
{
<div class="card-item">
<div class="categories">
<NavLink title="❌刪除" @onclick="@(async () => await DeleteAsync(item.Id))">❌</NavLink>
<NavLink title="📝編輯" @onclick="@(() => ShowBox(item))">📝</NavLink>
<NavLink target="_blank" href="@($"/category/{item.DisplayName}")">
<h3>@item.CategoryName</h3>
<small>(@item.Count)</small>
</NavLink>
</div>
</div>
}
<div class="card-item">
<div class="categories">
<NavLink><h3 @onclick="@(() => ShowBox())">📕~~~ 新增分類 ~~~📕</h3></NavLink>
</div>
</div>
</div>
}
else
{
<ErrorTip />
}
</div>
<Box OnClickCallback="@SubmitAsync" Open="@Open">
<div class="box-item">
<b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" />
</div>
<div class="box-item">
<b>CategoryName:</b><input type="text" @bind="@categoryName" @bind:event="oninput" />
</div>
</Box>
}
</AdminLayout>
@code {
/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;
/// <summary>
/// 新增或者更新時候的分類字段值
/// </summary>
private string categoryName, displayName;
/// <summary>
/// 更新分類的Id值
/// </summary>
private int id;
/// <summary>
/// API返回的分類列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryCategoryForAdminDto>> categories;
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
var token = await Common.GetStorageAsync("token");
Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
categories = await FetchData();
}
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> FetchData()
{
return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>>("/blog/admin/categories");
}
/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task DeleteAsync(int id)
{
Open = false;
// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n💥💢真的要干掉這個該死的分類嗎💢💥");
if (confirmed)
{
var response = await Http.DeleteAsync($"/blog/category?id={id}");
var result = await response.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
categories = await FetchData();
}
}
}
/// <summary>
/// 顯示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryCategoryForAdminDto dto = null)
{
Open = true;
id = 0;
// 新增
if (dto == null)
{
displayName = null;
categoryName = null;
}
else // 更新
{
id = dto.Id;
displayName = dto.DisplayName;
categoryName = dto.CategoryName;
}
}
/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
var input = new EditCategoryInput()
{
DisplayName = displayName.Trim(),
CategoryName = categoryName.Trim()
};
if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.CategoryName))
{
return;
}
var responseMessage = new HttpResponseMessage();
if (id > 0)
responseMessage = await Http.PutAsJsonAsync($"/blog/category?id={id}", input);
else
responseMessage = await Http.PostAsJsonAsync("/blog/category", input);
var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
categories = await FetchData();
Open = false;
}
}
}
友鏈管理
實現方式都是一樣的,這個就不多說了,直接上代碼。
先將API返回的接收參數和新增編輯的輸入參數添加一下。
//QueryFriendLinkForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class QueryFriendLinkForAdminDto : FriendLinkDto
{
/// <summary>
/// 主鍵
/// </summary>
public int Id { get; set; }
}
}
//EditFriendLinkInput.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class EditFriendLinkInput : FriendLinkDto
{
}
}
@page "/admin/friendlinks"
<AdminLayout>
@if (friendlinks == null)
{
<Loading />
}
else
{
<div class="post-wrap categories">
<h2 class="post-title">- FriendLinks -</h2>
@if (friendlinks.Success && friendlinks.Result.Any())
{
<div class="categories-card">
@foreach (var item in friendlinks.Result)
{
<div class="card-item">
<div class="categories">
<NavLink title="❌刪除" @onclick="@(async () => await DeleteAsync(item.Id))">❌</NavLink>
<NavLink title="📝編輯" @onclick="@(() => ShowBox(item))">📝</NavLink>
<NavLink target="_blank" href="@item.LinkUrl">
<h3>@item.Title</h3>
</NavLink>
</div>
</div>
}
<div class="card-item">
<div class="categories">
<NavLink><h3 @onclick="@(() => ShowBox())">📒~~~ 新增友鏈 ~~~📒</h3></NavLink>
</div>
</div>
</div>
}
else
{
<ErrorTip />
}
</div>
<Box OnClickCallback="@SubmitAsync" Open="@Open">
<div class="box-item">
<b>Title:</b><input type="text" @bind="@title" @bind:event="oninput" />
</div>
<div class="box-item">
<b>LinkUrl:</b><input type="text" @bind="@linkUrl" @bind:event="oninput" />
</div>
</Box>
}
</AdminLayout>
@code {
/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;
/// <summary>
/// 新增或者更新時候的友鏈字段值
/// </summary>
private string title, linkUrl;
/// <summary>
/// 更新友鏈的Id值
/// </summary>
private int id;
/// <summary>
/// API返回的友鏈列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>> friendlinks;
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
var token = await Common.GetStorageAsync("token");
Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
friendlinks = await FetchData();
}
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> FetchData()
{
return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>>("/blog/admin/friendlinks");
}
/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task DeleteAsync(int id)
{
Open = false;
// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n💥💢真的要干掉這個該死的分類嗎💢💥");
if (confirmed)
{
var response = await Http.DeleteAsync($"/blog/friendlink?id={id}");
var result = await response.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
friendlinks = await FetchData();
}
}
}
/// <summary>
/// 顯示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryFriendLinkForAdminDto dto = null)
{
Open = true;
id = 0;
// 新增
if (dto == null)
{
title = null;
linkUrl = null;
}
else // 更新
{
id = dto.Id;
title = dto.Title;
linkUrl = dto.LinkUrl;
}
}
/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
var input = new EditFriendLinkInput()
{
Title = title.Trim(),
LinkUrl = linkUrl.Trim()
};
if (string.IsNullOrEmpty(input.Title) || string.IsNullOrEmpty(input.LinkUrl))
{
return;
}
var responseMessage = new HttpResponseMessage();
if (id > 0)
responseMessage = await Http.PutAsJsonAsync($"/blog/friendlink?id={id}", input);
else
responseMessage = await Http.PostAsJsonAsync("/blog/friendlink", input);
var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
friendlinks = await FetchData();
Open = false;
}
}
}
截至目前為止,還剩下文章模塊的功能還沒做了,今天到這里吧,明天繼續剛,未完待續...