
最终效果如上图。
多页签使用BlazorStrap中的BSTabGroup、BSTabList组件,内嵌绑定的BSTab:
<div><NavMenu OnShowLog="ShowLog" OnShowModel="ShowModel" />
</div>
<div class="container"><BSTabGroup ShownEvent="@Shown" @ref="tabGroup"><BSTabList>@foreach (var tabInfo in tabInfoList){<BSTab InitialSelection="@IsTabSelected(tabInfo.Item3.FullName)" Selected="@IsTabSelected(tabInfo.Item3.FullName)" id="@tabInfo.Item3.FullName"><BSTabLabel>@tabInfo.Item1@if (!tabInfo.Item1.Equals("首页")){<span> </span><span class="text-danger" @onclick="@(()=>CloseTab(tabInfo.Item3.FullName))">x</span>}</BSTabLabel><BSTabContent>@tabInfo.Item2</BSTabContent></BSTab>}</BSTabList><BSTabSelectedContent /></BSTabGroup>
</div>
 
上面第一个div创建主菜单;第二个div声明了多页签。多页签自动遍历tabInfoList中的数据,绘制界面。BSTab没有实现可关闭的效果,因此使用span定义了一个关闭小按钮,点击页签名称后的x,关闭页签。对于首页页签,不提供关闭功能,用@if做了判断。BSTabGroup封装的还是不够到位,控制过程有绑定,有也设置属性的方式。Blazor资料也比较少,开发过程中需要查看代码找解决方法。
private List<Tuple<string, RenderFragment, Type>> tabInfoList = new List<Tuple<string, RenderFragment, Type>>();//初始化的时候,添加首页页签
protected override void OnInitialized(){Storage.SetItem("environment_uri", String.Empty);Storage.SetItem("environment_uri", UriHelper.BaseUri + "api");tabInfoList.Add(new Tuple<string, RenderFragment, Type>("首页", CreateDynamicComponent(typeof(BlazorCrud.Client.Pages.Index)),typeof(BlazorCrud.Client.Pages.Index)));}void ShowModel(Tuple<string, Type> modelInfo){var tabInfo = tabInfoList.FirstOrDefault(t => t.Item3 == modelInfo.Item2);if(tabInfo != null){//找到一打开页签,则激活即可ShowLog($"打开已存在页签  {selectedModel}");tabGroup.Selected = tabGroup.Tabs[tabInfoList.IndexOf(tabInfo)];StateHasChanged();return;}tabInfoList.Add(new Tuple<string, RenderFragment, Type>(modelInfo.Item1, CreateDynamicComponent(modelInfo.Item2), modelInfo.Item2));selectedModel = modelInfo.Item2.FullName;StateHasChanged();}
//基于Blazor的渲染原理,动态创建组件的时候,传递组件的Type,绘制时自动创建组件实例,输出组件的html和css
RenderFragment CreateDynamicComponent(Type type) => builder =>{var seq = 0;builder.OpenComponent(seq, type);builder.CloseComponent();};//关闭页签,这里直接从tabInfoList中删除对应的元素即可,界面会自动刷新void CloseTab(string id){var tab = tabInfoList.FirstOrDefault(t=>t.Item3.FullName.Equals(id));if(tab != null){tabInfoList.Remove(tab);if(tabInfoList.Count > 0){ShowModel(new Tuple<string, Type>(tabInfoList.Last().Item1, tabInfoList.Last().Item3));}}} 
blazor中组件间通信机制:子组件定义事件,父组件绑定响应方法。父组件引用子组件时用@ref定义子组件的名字,在代码中声明同名成员,即可得到子组件实例,直接调用子组件中的公共方法。
开发完毕发布到服务器上,需要源码中wwwroot目录、编译后的_framework目录(两者同级),请求后报错,还有一个_content目录,需要在vs调试环境下,启动程序页面,将页面另存为,获取到所有依赖文件,在根据运行错误提示,将依赖文件按要求放入_content的子目录中。即可在非iis服务器上发布成功。如浏览器提示错误,在根据错误提示,调整wwwroot、_framework、_content中文件的位置即可。
查看调试程序运行信息:右下角找到IIS Express小图标,右键,显示应用程序所在位置及url:

vs提供的发布功能:blazor客户端项目,右键,发布,发布到文件夹,即可获取到完整的目录。




















