基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(七)

系列文章

  1. 使用 abp cli 搭建项目

  2. 给项目瘦身,让它跑起来

  3. 完善与美化,Swagger登场

  4. 数据访问和代码优先

  5. 自定义仓储之增删改查

  6. 统一规范API,包装返回模型

  7. 再说Swagger,分组、描述、小绿锁

  8. 接入GitHub,用JWT保护你的API

  9. 异常处理和日志记录

  10. 使用Redis缓存数据

  11. 集成Hangfire实现定时任务处理

  12. 用AutoMapper搞定对象映射

  13. 定时任务最佳实战(一)

  14. 定时任务最佳实战(二)

  15. 定时任务最佳实战(三)

  16. 博客接口实战篇(一)

  17. 博客接口实战篇(二)

  18. 博客接口实战篇(三)

  19. 博客接口实战篇(四)

  20. 博客接口实战篇(五)

  21. Blazor实战系列(一)

  22. Blazor实战系列(二)

  23. Blazor实战系列(三)

  24. Blazor实战系列(四)

  25. Blazor实战系列(五)

  26. 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">-&nbsp;Tags&nbsp;-</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">-&nbsp;Categories&nbsp;-</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">-&nbsp;FriendLinks&nbsp;-</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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[数据结构-严蔚敏版]P64循环队列-队列的顺序存储结构

代码如下: #include <iostream> using namespace std;const int MAXQSIZE 10;typedef int ElemType;typedef struct {ElemType *base;int front;int rear; }SqQueue;bool initQueue(SqQueue &q) {q.base new ElemType[MAXQSIZE];if (!q.base) return false;q.fron…

BeetleX.FastHttpApi之Vuejs扩展

非常喜欢用vuejs,但又不想花时间去搞nodejs和webpack之类的&#xff0c;所以才有了BeetleX.FastHttpApi.VueExtend这样一个组件&#xff1b;组件的主要功能就是可以实现在vs.net中编写*.vue并直接引用到服务中&#xff0c;这样对于我这个习惯在vs.net写服务应的带来极大的方便性…

C++的new、delete需要注意的一点:使用危险函数导致的越界CRT detected that the application wrote to memory after end of heap

new、delete需要注意的一个特性 正常情况new一个数组之后&#xff0c;用delete释放是没有问题的。但是当对new得到的堆区进行越界的写入操作&#xff08;读操作不会&#xff09;将会导致delete时出现段错误&#xff0c;无法进行删除。如下面的程序所示&#xff1a; 数组大小只…

数据结构与算法专题——第四题 字符串相似度

这篇我们看看 最长公共子序列 的另一个版本&#xff0c;求字符串相似度(编辑距离)&#xff0c;我也说过了&#xff0c;这是一个非常实用的算法&#xff0c;在DNA对比&#xff0c;网页聚类等方面都有用武之地。一&#xff1a;概念对于两个字符串 A 和 B&#xff0c;通过基本的增…

[数据结构-严蔚敏版]P71串的抽象数据类型的定义

代码如下: #include <iostream> #include <string> using namespace std;typedef struct {char *ch;int length; }String;bool initString(String &s) {s.ch nullptr;s.length 0;return true; }bool strAssign(String &s, const char *ch) {int len st…

三分钟学会.NET Core Jwt 策略授权认证

一.前言大家好我又回来了&#xff0c;前几天讲过一个关于Jwt的身份验证最简单的案例&#xff0c;但是功能还是不够强大&#xff0c;不适用于真正的项目&#xff0c;是的&#xff0c;在真正面对复杂而又苛刻的客户中&#xff0c;我们会不知所措&#xff0c;就现在需要将认证授权…

[数据结构-严蔚敏版]P65离散事件模拟(银行客户的离散事件驱动模拟程序)

写这个简单玩意&#xff0c;居然花费了我6小时&#xff0c;唉!!!&#xff0c;还是太菜了! 中间已经起了放弃的念头了&#xff0c;最后还是坚持下来了! 总结&#xff1a; (1)漏了p p->next (2)队列删除元素的时候&#xff0c;删除的是最后一个忘记特判。 (3)写的时候太急了…

[温故知新] 编程原则和模式

写了这么多年代码&#xff0c;依旧做不好一个项目做好一个项目是人力、产品、业务、技术、运营的结合&#xff0c;可能还叠加一点时机的因素&#xff0c;就我们码农而言&#xff0c;工作就是搬砖&#xff0c;实现产品&#xff0c; 给业务提供支撑。“给祖传代码加 BUG 修 BUG”…

IntelliJ IDEA中快捷键大全+出现的问题

参照 文章目录1.Ctrl2.Alt3.Shift4.Ctrl Alt5.Ctrl Shift6.Alt Shift7.Ctrl Shift Alt8.其他idea如何将设置包名展开当出现右键&#xff0c;没有创建包选项时idea没有maven图标安装mysql和idea配置idea查看代码的最近修改人及时间1.Ctrl 快捷键介绍Ctrl F在当前文件进行…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(八)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

[数据结构-严蔚敏版]P95矩阵压缩-特殊矩阵的存储(对称矩阵,三角矩阵)

对称矩阵的存储&#xff1a; 代码如下: #include <iostream> using namespace std;int main() {int n;cin >> n;int *a;a new int[(n*(n 1)) / 2];for (int i 0; i < (n*(n 1)) / 2; i){cin >> a[i];}for (int i 1; i < n; i){for (int j 1; j…

微前端与项目实施方案研究

一、前言微前端(micro-frontends)是近几年在前端领域出现的一个新概念&#xff0c;主要内容是将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块&#xff0c;而在用户看来仍然是内聚的单个产品。微前端的理念源于微服务&#xff0c;是将庞大的整体拆成可控的小…

ASP.NET Core分布式项目实战(集成ASP.NETCore Identity)--学习笔记

任务24&#xff1a;集成ASP.NETCore Identity之前在 Index 页面写了一个 strong 标签&#xff0c;需要加个判断再显示&#xff0c;不然为空没有错误的时候也会显示if (!ViewContext.ModelState.IsValid) {<strong>Error""</strong><div asp-validatio…

Java中关于省略作用域报错问题分析

这个是很典型的作用域问题&#xff0c;if后如果省略那么if只作用于其后面的第一行代码 这时候如果这行代码只是个变量声明语句的话&#xff0c;这个变量是没有其他任何逻辑可以访问到的&#xff0c;因为作用域问题(如果有/&#xff0c;那么声明语句中声明的变量只在这个个内可用…

C#9就这么来了,.NET开发者该做点什么?

就在上周三10号&#xff0c;.NET5.0发布了第5个预览版&#xff0c;同时支持了C#9-preview&#xff01;是的&#xff0c;你没看错&#xff0c;虽然C# 8.0还未正式发布&#xff0c;但是通往C&#xff03;9的漫长道路却已经开始&#xff0c;这发展速度简直了&#xff01;C#语言的快…

Magicodes.IE在.NET Core中通过请求头导出多种格式文件

原文作者&#xff1a;HueiFeng前言在2.2里程碑中我们增加了一些新的功能,正如标题所写通过请求头进行导出我们不同格式的文件.下面我们来看一下如何使用.通过这种方式无论是对我们的数据多用途&#xff0c;还是说对我们的数据校验都做到了轻松易配。同时我们也将在本周发布2.3版…

使用DQL查询数据

文章目录DQL语言SELECT语法制定查询字段AS 子句作为别名DISTINCT关键字的使用使用表达式的列where条件语句逻辑操作符模糊查询 &#xff1a; 比较操作符连接查询JION自连接排序和分页子查询DQL语言 DQL( Data Query Language 数据查询语言 ) 查询数据库数据 , 如SELECT语句简…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(九)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

SilkierQuartz 1.0.21 发布, 是一个 Quartz.NET 的强大且简单的Web管理工具和承载组件...

SilkierQuartz 是一个新的合并了 Quartzmin 和 QuartzHostedService的组件!Quartz.NET 是一个完整的开源的任务规划系统&#xff0c;从小应用至大型企业级应用都可以适用.Quartzmin Quartzmin 是一个 Quartz.NET 的强大且简单的Web管理工具QuartzHostedService QuartzHostedSer…

用C#在STM32上写第一个Hello world

随着微软放弃.Net MF ,通过C#编写STM32 平台上的程序变得渺茫&#xff0c; 但是&#xff0c; 别着急&#xff0c; 目前至少有两个社区在做这件事情&#xff0c; 传承了微软的.Net MF , 一家是 nanoframework,另外一家比较封闭的是 GHI Electronics 地址是: https://github.com/…