领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

C# + Blazor Web入门实战:私人笔记(10)多标签页内容管理

nixiaole 2024-11-22 18:51:27 知识剖析 18 ℃

上一节我们完成了分类菜单的管理,本节开始着手内容管理。

内容列表布局

同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

简单说下:

TabsTabPane就是用来做多标签的,每个TabPane对应的就是一个标签页。

默认打开我们之前做的内容分类页,这个分类页是左树右表的形式,通过LayoutSider控件来完成,左边就是嵌入我们之前做的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,代码内容会随教程实时更新,大家有兴趣的话可以关注我,以获得最及时的更新。私信:

私人日记 可以获取相关链接;

大家阅读过程中有哪些看不懂或未尽兴的地方,可以在评论区留言,我会先记下来在后续的教程中找机会再说。

教程有帮助的话请大家帮忙关注、转发、扩散,能不能开专栏还需要你们的支持!

Tags:

最近发表
标签列表