网站首页 > 知识剖析 正文
上一节我们完成了分类菜单的管理,本节开始着手内容管理。
内容列表布局
同Diary.Win类似,我们希望的布局是左树右表的形式,并且,我们希望像浏览器一样采用多标签页的方式打开,便于我们在不同内容间粘贴或复制。所以现在需要对整体页面做下布局设计。
这块比较繁琐,我直接上代码:
<Tabs Type="@TabType.EditableCard" TabPosition="TabPosition.Top" Style="height: 100%;" @bind-ActiveKey="activeKey" OnAddClick="AddContent" OnClose="OnTabClose">
<TabPane Tab="内容分类" Key="list" Style="height: 100%;" Closable="false">
???????<Layout Class="site-layout-background">
???????????<Sider Width="200" Class="site-layout-background">
???????????????<ContextMenuPanel OnContextMenu="OnContextMenuFunction" OnClick="OnMenuTreeClickFunction">
???????????????????<Tree @ref="menuTree" TItem="DiaryTreeNode<Model.Category>" DataSource="@_trees" TitleExpression="x => x.DataItem.Entity.Name" ChildrenExpression="x => x.DataItem.Nodes" ShowLine="true">
???????????????????</Tree>
???????????????????<Menu Style="@menuStyle">
???????????????????????<MenuItem OnClick="CreateNode">
???????????????????????????@menu_create_text
???????????????????????</MenuItem>
???????????????????????<MenuItem OnClick="ModifyNode">
???????????????????????????修改节点
???????????????????????</MenuItem>
???????????????????????<MenuItem OnClick="DeleteNode">
???????????????????????????删除节点
???????????????????????</MenuItem>
???????????????????</Menu>
???????????????????<CategoryEdit @ref="categoryEditor" Value="@curCategory" OnSave="@SaveCategory"></CategoryEdit>
???????????????</ContextMenuPanel>
???????????</Sider>
???????????<Layout Style="padding: 0px; height: 100%;" Class="site-layout-background">
内容列表
???????????</Layout>
???????</Layout>
???</TabPane>
???@foreach (var pane in panes)
???{
???????<TabPane Key="@pane.Key" Tab="@pane.Title" Closable="@pane.Closable">
???????????<iframe style="width:100%; min-height:850px;" frameborder="0" scrolling="no" id="iframe" src="@pane.Url" />
???????</TabPane>
???}
</Tabs>
@code
{
???#region 多标签
???string activeKey = "";
???public record Pane(string Title, string Url, string Key, bool Closable = true);
???List<Pane> panes = new List<Pane>();
???private void AddContent()
???{
???????if (menuTree.SelectedNode == null) return;
???????string url = "/Counter";
???????string key = Guid.NewGuid().ToString();
???????panes.Add(new Pane(#34;新建内容:" + menuTree.SelectedNode.DataItem.Entity.Name, url, key, true));
???????activeKey = key;
???}
???void OnTabClose(string key)
???{
???????foreach (Pane p in panes)
???????{
???????????if (p.Key == key)
???????????{
???????????????panes.Remove(p);
???????????????break;
???????????}
???????}
???}
???#endregion
简单说下:
Tabs和TabPane就是用来做多标签的,每个TabPane对应的就是一个标签页。
默认打开我们之前做的内容分类页,这个分类页是左树右表的形式,通过Layout和Sider控件来完成,左边就是嵌入我们之前做的ContextMenuPanel,右侧是内容列表,等下我们去实现。
除去默认打开的内容分类页,就是动态创建的panes,这个在code中有定义。
然后Tabs中增加添加和关闭事件,用activeKey来控制当前显示的是哪个Pane。
基本思路就是这样,代码看起来比较多,但其实也都很好理解,基本上没什么技术难点。
值得一提的是record关键字,这个是C# 9的新特性,我之前也没用过,就是一种语法糖,基本上跟定义一个类差不多,但可以用一行语句搞定。
看下演示效果:
内容列表布局
上面完成的是多标签及左边树的内容,接下来来实现右侧内容列表的布局。内容仍然较多较杂,还是直接上代码。在上面内容列表处,替换成如下内容:
@if (_contents.Count == 0)
{
<p>暂无内容</p>
???????????????}
???????????????else
???????????????{
???????????????????<Table TItem="Model.Content" DataSource="@_contents" Total="_total" @bind-PageIndex="_pageIndex" @bind-PageSize="_pageSize">
???????????????????????<Column @bind-Field="@context.Id" />
???????????????????????<Column @bind-Field="@context.Title" Sortable />
???????????????????????<Column @bind-Field="@context.RealTime" Sortable />
???????????????????????<ActionColumn>
???????????????????????????<Space>
???????????????????????????????<SpaceItem><Button Danger OnClick="() => Edit(context)">编辑</Button></SpaceItem>
???????????????????????????</Space>
???????????????????????</ActionColumn>
???????????????????</Table>
???????????????}
code中加入:
#region 内容列表
???List<Model.Content> _contents = new List<Model.Content>();
???int _pageIndex = 1;
???int _pageSize = 5;
???int _total = 0;
???private void Edit(Model.Content item)
???{
???????foreach (Pane p in panes)
???????{
???????????if (p.Key == item.Id.ToString())
???????????{
???????????????activeKey = item.Id.ToString();
???????????????return;
???????????}
???????}
???????var key = item.Id.ToString();
???????activeKey = key;
???????string url = "/CategoryEdit?id=" + item.Id;
???????panes.Add(new Pane(#34;内容管理:{item.Title}", url, key, true));
???}
???#endregion
async void OnTreeNodeSelect(TreeEventArgs<DiaryTreeNode<Model.Category>> args)
{
if (args.Node == null) return;
_contents.Clear();
string pms = string.Format("cid={0}", args.Node.DataItem.Entity.Id);
string url = Tools.get_api_url("content", "get_by_category", pms);
string content_data = await Http.GetStringAsync(url);
_contents = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Model.Content>>(content_data);
_total = _contents.Count;
StateHasChanged();
}
Tree控件中增加OnSelect事件:
<Tree @ref="menuTree" TItem="DiaryTreeNode<Model.Category>" DataSource="@_trees" TitleExpression="x => x.DataItem.Entity.Name" ChildrenExpression="x => x.DataItem.Nodes" OnSelect="OnTreeNodeSelect" ShowLine="true"></Tree>
WebAPI中增加content的接口:
public string _(string biztype, string function)
{
string ret = "";
switch (biztype)
{
case "category":
ret = category(function);
break;
case "content":
ret = content(function);
break;
}
return ret;
}
string content(string function)
{
string ret = "";
switch (function)
{
case "get_by_category":
{
int cid = Request.getQueryInt("cid");
List<Model.Content> contents = BAL.Content.query_by_categoryId(cid);
ret = Newtonsoft.Json.JsonConvert.SerializeObject(contents);
}
break;
case "insert":
{
string content = Request.getPostData();
Model.Content model = Newtonsoft.Json.JsonConvert.DeserializeObject<Model.Content>(content);
if (model != null)
{
if (BAL.Content.Insert(model) != null)
{
ret = "ok";
}
}
}
break;
case "update":
{
string content = Request.getPostData();
Model.Content category = Newtonsoft.Json.JsonConvert.DeserializeObject<Model.Content>(content);
if (category != null)
{
if (BAL.Content.Modify(category))
{
ret = "ok";
}
}
}
break;
case "delete":
{
int id = Request.getQueryInt("id");
if (id > 0)
{
if (BAL.Content.Delete(id))
{
ret = "ok";
}
}
}
break;
}
return ret;
}
虽然内容较多,但是知识点在之前的文章中都讲过。现在无非就是把这些知识点再应用到不同的控件上。看到这里,其实大家都应该大概了解Blazor的基本运行机制了,套路都差不多,都没什么难度,我就不讲了。直接上效果:
最近这几节自我感觉有点敷衍了事,说实话这种编码确实感觉没有什么成就感,不需要做太多的思考,无非就是控件加事件、绑数据,然后分别对不同的操作做处理,完全就是体力活。这种工作是在所有编程中我最不愿意做的,不知道有多少人有同感的:)
不过虽然无趣,但本节的进展却是最快的。上一节突破了分类管理的难点,后面基本就是一马平川了。希望大家能够坚持下来,下一节,我们将进行富文本编辑器的编码。
---------------------------------------
本教程项目源码已作为开源项目加入到Gitee,代码内容会随教程实时更新,大家有兴趣的话可以关注我,以获得最及时的更新。私信:
私人日记 可以获取相关链接;
大家阅读过程中有哪些看不懂或未尽兴的地方,可以在评论区留言,我会先记下来在后续的教程中找机会再说。
教程有帮助的话请大家帮忙关注、转发、扩散,能不能开专栏还需要你们的支持!
- 上一篇: 初学者:HTML+CSS静态网页开发——网页布局
- 下一篇: Qt的常用控件
猜你喜欢
- 2024-11-22 SpringBoot与Loki的那些事
- 2024-11-22 Qt的常用控件
- 2024-11-22 初学者:HTML+CSS静态网页开发——网页布局
- 2024-11-22 力扣 C++11题解系列-083 柱状图中最大的矩形
- 2024-11-22 尝试一下使用 Vitest 进行组件测试,确实很香
- 2024-11-22 uniapp经验-总结1
- 2024-11-22 「CSS」 栅格化布局
- 2024-11-22 C# + Blazor Web入门实战:私人笔记(8)创建分类编辑组件
- 2024-11-22 2.6「HarmonyOS鸿蒙开发」定位布局PositionLayout
- 2024-11-22 使用CSS实现苹果官网文字渐入效果
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)