Enterprise-level component library based on Bootstrap and Blazor

star nuget master download license repo commit

Tabs 标签页

分隔内容上有关联但属于不同类别的数据集合。

Tab 组件从设计上采用模板的设计形式,使用本组件时通过将 TabItem 子组件添加到 TabItems 模板中即可

基础用法

基础的、简洁的标签页。

Demo
我是用户管理
我是菜单管理
我是角色管理
我是部门管理

选项卡样式

通过 IsCard="true" 设置选项卡样式的标签页。

Demo
我是用户管理
我是菜单管理
我是角色管理
我是部门管理

卡片化

通过 IsBorderCard="true" 设置卡片化的标签页。

Demo
我是用户管理
我是菜单管理
我是角色管理
我是部门管理

图标

通过设置 TabItem 组件的 Icon 属性对标签页设置图标

Demo
我是用户管理
我是菜单管理
我是角色管理
我是部门管理

关闭

通过设置 ShowClose 属性对标签页显示关闭按钮

Demo
我是用户管理
我是菜单管理
我是角色管理
我是部门管理

位置

通过设置 Placement 属性更改标签位置,更改为左右时会出现上下滚动小箭头进行 TabItem 切换

Demo

我是用户管理
我是菜单管理
我是角色管理
我是系统日志
我是登录日志
我是定时任务管理
Tab 分割线
我是用户管理
我是菜单管理
我是角色管理
我是系统日志
我是登录日志
我是定时任务管理
Tab 分割线
我是用户管理
我是菜单管理
我是角色管理
我是系统日志
我是登录日志
我是定时任务管理

自定义增加标签页触发器

通过调用组件 api 动态添加/删除 TabItem

Demo

我是用户管理
我是菜单管理
我是角色管理
我是部门管理
我是任务管理

内置其他组件

TabItem 中内置其他组件

Demo

Tab 组件各个面板内的内容默认是保持状态的,本例中面板切换时保持原有数据

Counter

Current count: 0

Weather forecast

This component demonstrates fetching data from a service.

Date Temp. (C) Temp. (F) Summary
4/24/2021 46 114 Hot
4/25/2021 -12 11 Chilly
4/26/2021 8 46 Sweltering
4/27/2021 1 33 Mild
4/28/2021 -8 18 Hot

程序动态添加 TabItem 面板

通过此功能可以实现侧边栏中点击菜单链接,右侧数据区上部呈现多标签

Demo

本例中右侧 Tab 面板内状态一直保持,关闭后重新打开时组件重新加载

网站 Header

实战 Tab 组件

通过设置 ShowExtendButtons 属性为 true,开启组件左右移动按钮与关闭下拉菜单,实战中非常实用

Demo

我是用户管理
我是菜单管理
我是角色管理
我是部门管理
我是任务管理

Attributes

Loading...

Methods

Loading...
@page "/tabs"

<h3>Tabs 标签页</h3>

<h4>分隔内容上有关联但属于不同类别的数据集合。</h4>

<p>Tab 组件从设计上采用模板的设计形式,使用本组件时通过将 <code>TabItem</code> 子组件添加到 <code>TabItems</code> 模板中即可</p>

<Tips>
    <p>
        <code>Tab</code> 组件一般有两种用法:
        <ul class="ul-demo">
            <li>作为数据分割</li>
            <li>页面导航</li>
        </ul>
        <div>本组件默认行为为数据分割,点击 <code>TabItem</code> 标题时并不会进行导航行为,如果需要进行地址栏导航时请设置 <code>ClickTabToNavigation</code> 属性为 <code>true</code>,此时点击 <code>TabItem</code> 标题时地址栏将会重定向导航,多用于后台管理系统与 <code>Menu</code> 组件进行联动使用,实战可参考 <a href="layout-page" target="_blank">后台模板模拟器</a> 中的 <code>多标签</code> 模式,如果有 <code>Razor 组件</code> 在额外的程序集中时,请正确设置 <code>AdditionalAssemblies</code> 属性值,以便标签组件内路由正确解析,相关文档 <a href="https://docs.microsoft.com/zh-cn/aspnet/core/blazor/fundamentals/routing?WT.mc_id=DT-MVP-5004174&view=aspnetcore-3.1#route-to-components-from-multiple-assemblies" target="_blank">[传送门]</a></div>
    </p>
    <p>本组件会根据宽度高度等进行自适应适配,适当的时候可以出现左右或者上下的滚动箭头</p>
</Tips>

<Block Title="基础用法" Introduction="基础的、简洁的标签页。">
    <Tab>
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理">
            <div>我是部门管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="选项卡样式" Introduction='通过 <code>IsCard="true"</code> 设置选项卡样式的标签页。'>
    <Tab IsCard="true">
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理">
            <div>我是部门管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="卡片化" Introduction='通过 <code>IsBorderCard="true"</code> 设置卡片化的标签页。'>
    <Tab IsBorderCard="true">
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理">
            <div>我是部门管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="图标" Introduction='通过设置 <code>TabItem</code> 组件的 <code>Icon</code> 属性对标签页设置图标'>
    <Tab IsCard="true">
        <TabItem Text="用户管理" Icon="fa fa-user">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理" Icon="fa fa-dashboard">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理" Icon="fa fa-sitemap">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理" Icon="fa fa-bank">
            <div>我是部门管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="关闭" Introduction='通过设置 <code>ShowClose</code> 属性对标签页显示关闭按钮'>
    <Tips>
        <p><code>Tab</code> 组件开启 <code>ShowClose</code> 后,<code>TabItem</code> 属性 <code>Closable</code> 可对标签页单独设置是否可关闭,默认为 <code>true</code>;本例中 <b>用户管理</b> 标签页不提供关闭功能</p>
    </Tips>
    <Tab IsCard="true" ShowClose="true">
        <TabItem Text="用户管理" Icon="fa fa-user" Closable="false">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理" Icon="fa fa-dashboard">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理" Icon="fa fa-sitemap">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理" Icon="fa fa-bank">
            <div>我是部门管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="位置" Introduction="通过设置 <code>Placement</code> 属性更改标签位置,更改为左右时会出现上下滚动小箭头进行 <code>TabItem</code> 切换">
    <p class="text-center">
        <div class="btn-group">
            <button class="btn btn-primary" @onclick="e => SetPlacement(Placement.Top)">Top</button>
            <button class="btn btn-primary" @onclick="e => SetPlacement(Placement.Right)">Right</button>
            <button class="btn btn-primary" @onclick="e => SetPlacement(Placement.Bottom)">Bottom</button>
            <button class="btn btn-primary" @onclick="e => SetPlacement(Placement.Left)">Left</button>
        </div>
    </p>
    <Tab Placement="@BindPlacement" Height="200">
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="系统日志">
            <div>我是系统日志</div>
        </TabItem>
        <TabItem Text="登录日志">
            <div>我是登录日志</div>
        </TabItem>
        <TabItem Text="定时任务管理">
            <div>我是定时任务管理</div>
        </TabItem>
    </Tab>
    <Divider Text="Tab 分割线"></Divider>
    <Tab Placement="@BindPlacement" IsCard="true" Height="200">
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="系统日志">
            <div>我是系统日志</div>
        </TabItem>
        <TabItem Text="登录日志">
            <div>我是登录日志</div>
        </TabItem>
        <TabItem Text="定时任务管理">
            <div>我是定时任务管理</div>
        </TabItem>
    </Tab>
    <Divider Text="Tab 分割线"></Divider>
    <Tab Placement="@BindPlacement" IsBorderCard="true" Height="200">
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="系统日志">
            <div>我是系统日志</div>
        </TabItem>
        <TabItem Text="登录日志">
            <div>我是登录日志</div>
        </TabItem>
        <TabItem Text="定时任务管理">
            <div>我是定时任务管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="自定义增加标签页触发器" Introduction="通过调用组件 api 动态添加/删除 <code>TabItem</code>">
    <p>
        <Button Icon="fa fa-plus-circle" OnClickWithoutRender="@(() => AddTab(TabSet))" Text="添加">
        </Button>
        <Button Color="Color.Danger" Icon="fa fa-minus-circle" IsDisabled="@RemoveEndable" OnClickWithoutRender="@(() => RemoveTab(TabSet))" Text="移除">
        </Button>
    </p>
    <Tab IsBorderCard="true" @ref="TabSet">
        <TabItem Text="用户管理">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理">
            <div>我是部门管理</div>
        </TabItem>
        <TabItem Text="任务管理">
            <div>我是任务管理</div>
        </TabItem>
    </Tab>
</Block>

<Block Title="内置其他组件" Introduction="<code>TabItem</code> 中内置其他组件">
    <p>
        <code>Tab</code> 组件各个面板内的内容默认是保持状态的,本例中面板切换时保持原有数据
    </p>
    <Tab IsBorderCard="true">
        <TabItem Text="计数组件">
            <Counter></Counter>
        </TabItem>
        <TabItem Text="天气预报">
            <FetchData></FetchData>
        </TabItem>
    </Tab>
</Block>

<Block Title="程序动态添加 TabItem 面板" Introduction="通过此功能可以实现侧边栏中点击菜单链接,右侧数据区上部呈现多标签">
    <p>
        本例中右侧 <code>Tab</code> 面板内状态一直保持,关闭后重新打开时组件重新加载
    </p>
    <Layout SideWidth="120px" style="min-height: 180px; border: 1px solid #ddd; border-radius: 4px;">
        <Header>
            <div class="header">网站 Header</div>
        </Header>
        <Side>
            <div style="border-right: 1px solid #e6e6e6; height: 100%; overflow: auto; padding: 1rem 0; background-color: #f8f9fa;">
                <Menu Items="@GetSideMenuItems()" IsVertical="true" OnClick="@OnClickMenuItem" @ref="TabMenu" />
            </div>
        </Side>
        <Main>
            <div class="tab-main-demo">
                <Tab @ref="TabSetMenu" ShowClose="true">
                </Tab>
            </div>
        </Main>
    </Layout>
</Block>

<Block Title="实战 Tab 组件" Introduction="通过设置 <code>ShowExtendButtons</code> 属性为 <code>true</code>,开启组件左右移动按钮与关闭下拉菜单,实战中非常实用">
    <Tips>通过 <b>添加</b> <b>删除</b> 按钮动态调整 <code>TabItem</code> 数量,使其超出容器数量查看,左右移动效果;<b>用户管理</b> 设置为不可关闭;功能按钮无法关闭这个标签页</Tips>
    <p>
        <button type="button" class="btn btn-outline-primary" @onclick="@(e => AddTab(TabSet2))">
            <i class="fa fa-plus-circle"></i>
            <span>添加</span>
        </button>
        <button type="button" class="btn btn-outline-danger" disabled="@((TabSet2?.Items.Count() > 4) ? null : "true")" @onclick="@(e => RemoveTab(TabSet2!))">
            <i class="fa fa-minus-circle"></i>
            <span>移除</span>
        </button>
    </p>
    <Tab ShowExtendButtons="true" ShowClose="true" @ref="TabSet2">
        <TabItem Text="用户管理" Closable="false">
            <div>我是用户管理</div>
        </TabItem>
        <TabItem Text="菜单管理">
            <div>我是菜单管理</div>
        </TabItem>
        <TabItem Text="角色管理">
            <div>我是角色管理</div>
        </TabItem>
        <TabItem Text="部门管理">
            <div>我是部门管理</div>
        </TabItem>
        <TabItem Text="任务管理">
            <div>我是任务管理</div>
        </TabItem>
    </Tab>
</Block>

<AttributeTable Items="@GetAttributes()" />

<MethodTable Items="@GetMethods()" />
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using BootstrapBlazor.Components;
using BootstrapBlazor.Shared.Common;
using BootstrapBlazor.Shared.Pages.Components;
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;

namespace BootstrapBlazor.Shared.Pages
{
    /// <summary>
    /// 
    /// </summary>
    public sealed partial class Tabs
    {
        [NotNull]
        private Tab? TabSet { get; set; }

        [NotNull]
        private Tab? TabSet2 { get; set; }

        /// <summary>
        /// OnInitialized 方法
        /// </summary>
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            await base.OnAfterRenderAsync(firstRender);

            if (firstRender)
            {
                var menuItem = TabMenu?.Items.FirstOrDefault();
                if (menuItem != null)
                {
                    await InvokeAsync(() =>
                    {
                        var _ = TabMenu?.OnClick?.Invoke(menuItem);
                    });
                }
            }
        }

        private static Task AddTab(Tab tabset)
        {
            var text = $"Tab {tabset.Items.Count() + 1}";
            tabset.AddTab(new Dictionary<string, object?>
            {
                [nameof(TabItem.Text)] = text,
                [nameof(TabItem.IsActive)] = true,
                [nameof(TabItem.ChildContent)] = new RenderFragment(builder =>
                {
                    var index = 0;
                    builder.OpenElement(index++, "div");
                    builder.AddContent(index++, $"我是新建的 Tab 名称是 {text}");
                    builder.CloseElement();
                })
            });
            return Task.CompletedTask;
        }

        private bool RemoveEndable => (TabSet?.Items.Count() ?? 4) < 4;

        private static Task RemoveTab(Tab tabset)
        {
            if (tabset.Items.Count() > 4)
            {
                var item = tabset.Items.Last();
                tabset.RemoveTab(item);
            }
            return Task.CompletedTask;
        }

        private Placement BindPlacement = Placement.Top;

        private void SetPlacement(Placement placement)
        {
            BindPlacement = placement;
        }

        private static IEnumerable<MenuItem> GetSideMenuItems()
        {
            return new List<MenuItem>
            {
                new MenuItem() { Text = "计数器"  },
                new MenuItem() { Text = "天气预报" }
            };
        }

        [NotNull]
        private Tab? TabSetMenu { get; set; }

        [NotNull]
        private Menu? TabMenu { get; set; }

        private Task OnClickMenuItem(MenuItem item)
        {
            var text = item.Text;
            var tabItem = TabSetMenu.Items.FirstOrDefault(i => i.Text == text);
            if (tabItem == null) AddTabItem(text ?? "");
            else TabSetMenu.ActiveTab(tabItem);
            return Task.CompletedTask;
        }

        private void AddTabItem(string text) => TabSetMenu.AddTab(new Dictionary<string, object?>
        {
            [nameof(TabItem.Text)] = text,
            [nameof(TabItem.IsActive)] = true,
            [nameof(TabItem.ChildContent)] = text == "计数器" ? BootstrapDynamicComponent.CreateComponent<Counter>().Render() : BootstrapDynamicComponent.CreateComponent<FetchData>().Render()
        });

        /// <summary>
        /// 获得属性方法
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<AttributeItem> GetAttributes() => new AttributeItem[]
        {
            // TODO: 移动到数据库中
            new AttributeItem() {
                Name = "IsBorderCard",
                Description = "是否为带边框卡片样式",
                Type = "boolean",
                ValueList = "true/false",
                DefaultValue = "false"
            },
            new AttributeItem() {
                Name = "IsCard",
                Description = "是否为卡片样式",
                Type = "boolean",
                ValueList = "true/false",
                DefaultValue = "false"
            },
            new AttributeItem() {
                Name = "ShowClose",
                Description = "是否显示关闭按钮",
                Type = "boolean",
                ValueList = "true/false",
                DefaultValue = "false"
            },
            new AttributeItem() {
                Name = "ShowExtendButtons",
                Description = "是否显示扩展按钮",
                Type = "boolean",
                ValueList = " — ",
                DefaultValue = "false"
            },
            new AttributeItem() {
                Name = "ClickTabToNavigation",
                Description = "点击标题时是否导航",
                Type = "boolean",
                ValueList = "true/false",
                DefaultValue = "false"
            },
            new AttributeItem() {
                Name = "Placement",
                Description = "设置标签位置",
                Type = "Placement",
                ValueList = "Top|Right|Bottom|Left",
                DefaultValue = "Top"
            },
            new AttributeItem() {
                Name = "Height",
                Description = "设置标签高度",
                Type = "int",
                ValueList = " — ",
                DefaultValue = "0"
            },
            new AttributeItem() {
                Name = "Items",
                Description = "TabItem 集合",
                Type = "IEnumerable<TabItemBase>",
                ValueList = " — ",
                DefaultValue = " — "
            },
            new AttributeItem() {
                Name = "ChildContent",
                Description = "ChildContent 模板",
                Type = "RenderFragment",
                ValueList = " — ",
                DefaultValue = " — "
            },
            new AttributeItem() {
                Name = "AdditionalAssemblies",
                Description = "额外程序集合,用于初始化路由",
                Type = "IEnumerable<Assembly>",
                ValueList = " — ",
                DefaultValue = " — "
            },
            new AttributeItem() {
                Name = "OnClickTab",
                Description = "点击 TabItem 标题时回调委托方法",
                Type = "Func<TabItem, Task>",
                ValueList = " — ",
                DefaultValue = " — "
            }
        };

        /// <summary>
        /// 获得方法
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<MethodItem> GetMethods() => new MethodItem[]
        {
            // TODO: 移动到数据库中
            new MethodItem() {
                Name = "AddTab",
                Description = "添加 TabItem 到 Tab 中",
                Parameters = "TabItem",
                ReturnValue = " — "
            },
            new MethodItem() {
                Name = "RemoveTab",
                Description = "移除 TabItem",
                Parameters = "TabItem",
                ReturnValue = " — "
            },
            new MethodItem() {
                Name = "ActiveTab",
                Description = "设置指定 TabItem 为激活状态",
                Parameters = "TabItem",
                ReturnValue = " — "
            },
            new MethodItem() {
                Name = "ClickPrevTab",
                Description = "切换到上一个标签方法",
                Parameters = "",
                ReturnValue = "Task"
            },
            new MethodItem() {
                Name = "ClickNextTab",
                Description = "切换到下一个标签方法",
                Parameters = "",
                ReturnValue = "Task"
            },
            new MethodItem() {
                Name = "CloseCurrentTab",
                Description = "关闭当前标签页方法",
                Parameters = "",
                ReturnValue = "Task"
            },
            new MethodItem() {
                Name = "CloseOtherTabs",
                Description = "关闭其他标签页方法",
                Parameters = "",
                ReturnValue = "Task"
            },
            new MethodItem() {
                Name = "CloseAllTabs",
                Description = "关闭所有标签页方法",
                Parameters = "",
                ReturnValue = "Task"
            },
        };
    }
}

B 站相关视频链接

暂无

交流群

QQ群:BootstrapAdmin & Blazor 795206915(满) 675147445 欢迎加群讨论
Themes
Bootstrap
Ant Design (完善中)
Bluma (完善中)
LayUI (完善中)
An error has occurred. This application may no longer respond until reloaded. Reload