页面开发之博客文章列表 介绍了文章列表的开发,页面中左侧是分类列表,右侧是该分类下的文章,这个布局乍看还是不错的,不过考虑到本项目支持多级分类,但分类列表只会机械式的把所有分类都显示出来,无法体现分类的层级结构且占用了很大的页面纵向空间,因此本文将对分类列表进行改造,使之能够体现多级分类、节省页面空间。
(实在不行还能把它代码clone下来魔改)
安装
依赖bower,如果没有bower的话需要先安装
npm install -g bower
然后在目录下执行以下命令安装依赖
npm install bootstrap-treeview
因为我们的静态资源都在下,所以npm安装的前端资源还需要通过gulp工具自动复制到里。
//使用 npm 下载的前端组件包
const libs = [
// ...
{name: "bootstrap-treeview", dist: "./node_modules/bootstrap-treeview/dist/**/*.*"},
];
然后执行gulp任务即可
gulp move
完成之后可以看到下已经多了一个目录了
用法
正式开始前,先来了解一下这个组件的用法
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap-treeview/dist/bootstrap-treeview.min.js"></script>
在网页里放一个容器
<div id="categories">
根据官方例子,使用js激活组件
const instance = $('#categories'.treeview({
data: collections,
};
格式如下
const collections = [
{
text: 'Parent 1',
href: '#parent1',
nodes: [
{
text: 'Child 1',
href: '#child1',
nodes: [
{
text: 'Grandchild 1',
href: '#grandchild1',
},
{
text: 'Grandchild 2',
href: '#grandchild2',
}
]
},
{
text: 'Child 2',
href: '#child2',
}
]
},
{
text: 'Parent 2',
href: '#parent2',
},
{
text: 'Parent 3',
href: '#parent3',
},
{
text: 'Parent 4',
href: '#parent4',
},
{
text: 'Parent 5',
href: '#parent5',
}
];
官网的默认效果
const instance = $('#categories'.treeview({
data: collections,
collapseIcon: "fa fa-caret-down",
expandIcon: "fa fa-caret-right",
emptyIcon: 'fa fa-circle-o',
};
处理分类数据
为了方便使用这个组件,我们需要在后端把分类层级包装成这个组件需要的形式。
public class CategoryNode {
public string text { get; set; } = "";
public string href { get; set; } = "";
public List<CategoryNode>? nodes { get; set; }
}
然后在里新增一个方法,用来生成分类的树结构,为了代码编写方便,我直接用递归来实现。
public List<CategoryNode>? GetNodes(int parentId = 0 {
var categories = _cRepo.Select
.Where(a => a.ParentId == parentId.ToList(;
if (categories.Count == 0 return null;
return categories.Select(category => new CategoryNode {
text = category.Name,
nodes = GetNodes(category.Id
}.ToList(;
}
这样输出来的数据就是这样
[
{
"text": "Android开发",
"href": "",
"nodes": null
},
{
"text": "AspNetCore",
"href": "",
"nodes": [
{
"text": "Asp-Net-Core学习笔记",
"href": "",
"nodes": null
},
{
"text": "Asp-Net-Core开发笔记",
"href": "",
"nodes": null
}
]
}
]
哦差点忘了还得给每个节点加上参数
不过我们这个方法是写在Service里,并没有实例,这时只能用依赖注入的方式,不过我在Stack Overflow上看到一个说法是,AspNetCore3.x之后,用更好。
builder.Services.AddHttpContextAccessor(;
然后依赖注入
private readonly IHttpContextAccessor _accessor;
private readonly LinkGenerator _generator;
public CategoryService(IHttpContextAccessor accessor, LinkGenerator generator {
_accessor = accessor;
_generator = generator;
}
修改上面那个方法,在初始化器里加上
href = _generator.GetUriByAction(
_accessor.HttpContext!,
nameof(BlogController.List,
"Blog",
new {categoryId = category.Id}
具体代码可以看GitHub:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Services/CategoryService.cs
{
"text": "Android开发",
"href": "http://localhost:5038/Blog/List?categoryId=2",
"nodes": null
}
前端渲染
数据准备好了,这时遇到一个问题,数据是要放到js中处理的,那我要用之类的异步请求来获取分类数据再显示树形分类吗?这样的好处是写起来比较直观,然而我们项目的博客网站是后端渲染,现在博客列表页面混入了异步请求,会导致割裂感,右边部分的文章列表服务端渲染出来在浏览器上展示了,左侧的分类还要异步去请求。
开始吧~
public List<CategoryNode> CategoryNodes { get; set; }
// 将上面的分类层级数据转换成Json字符串
public string CategoryNodesJson => JsonSerializer.Serialize(
CategoryNodes,
new JsonSerializerOptions {Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping}
;
然后修改一下Controller,先依赖注入
public IActionResult List(int categoryId = 0, int page = 1, int pageSize = 5 {
var categories = _categoryRepo.Where(a => a.Visible
.IncludeMany(a => a.Posts.ToList(;
categories.Insert(0, new Category {Id = 0, Name = "All", Posts = _postRepo.Select.ToList(};
return View(new BlogListViewModel {
CurrentCategory = categoryId == 0 ? categories[0] : categories.First(a => a.Id == categoryId,
CurrentCategoryId = categoryId,
Categories = categories,
// 增加这一行
CategoryNodes = _categoryService.GetNodes(,
Posts = _postService.GetPagedList(new PostQueryParameters {
CategoryId = categoryId,
Page = page,
PageSize = pageSize,
OnlyPublished = true
}
};
}
最后一步,修改View,在底部加入js引用和一些js代码,treeview组件的配置我已经封装成方法,可以直接使用。
@section bottom {
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap-treeview/dist/bootstrap-treeview.min.js"></script>
<script src="~/js/blog-list.js"></script>
<script>
const categories = '@Html.Raw(Model.CategoryNodesJson'
initTreeView(categories;
</script>
}
View的关键代码就这几行,完整代码可见:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Web/Views/Blog/List.cshtml
最终效果
不能高亮显示当前所选分类
没有实现分类文章数量显示(原来的版本是有的)
无法自定义样式,存在下划线不美观
..。
这几个问题留着后面优化吧~ 暂时先折腾到这里…
博客项目的开发已经基本完成,项目代码完全开源,有兴趣的朋友可以点个star~
博客后端+前台项目地址:
- https://github.com/Deali-Axy/StarBlog
管理后台前端项目地址:https://github.com/Deali-Axy/StarBlog-Admin