网站建设运营费用包括哪些37网页游戏中心
系列文章
- 使用 abp cli 搭建项目 
- 给项目瘦身,让它跑起来 
- 完善与美化,Swagger登场 
- 数据访问和代码优先 
- 自定义仓储之增删改查 
- 统一规范API,包装返回模型 
- 再说Swagger,分组、描述、小绿锁 
- 接入GitHub,用JWT保护你的API 
- 异常处理和日志记录 
- 使用Redis缓存数据 
- 集成Hangfire实现定时任务处理 
- 用AutoMapper搞定对象映射 
- 定时任务最佳实战(一) 
- 定时任务最佳实战(二) 
- 定时任务最佳实战(三) 
- 博客接口实战篇(一) 
- 博客接口实战篇(二) 
- 博客接口实战篇(三) 
- 博客接口实战篇(四) 
- 博客接口实战篇(五) 
- Blazor实战系列(一) 
- Blazor实战系列(二) 
- Blazor实战系列(三) 
- Blazor实战系列(四) 
- Blazor实战系列(五) 
- Blazor实战系列(六) 
上一篇完成了后台分类模块的所有功能,本篇继续将标签模块和友情链接模块的增删改查完成。
标签管理
实现方式和之前的分类管理是一样的,在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);elseresponseMessage = 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);elseresponseMessage = 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);elseresponseMessage = await Http.PostAsJsonAsync("/blog/friendlink", input);var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();if (result.Success){friendlinks = await FetchData();Open = false;}}
}
截至目前为止,还剩下文章模块的功能还没做了,今天到这里吧,明天继续刚,未完待续...
开源地址:https://github.com/Meowv/Blog/tree/blog_tutorial
