Enterprise-level component library based on Bootstrap and Blazor

star nuget master download license repo commit

Table 表格

常用于单表维护,通过属性配置实现简单的增、删、改、查、排序、过滤、搜索等常用功能,通过 Template 的高级用法能实现非常复杂的业务需求功能

设置 ShowSearch 显示查询组件

Demo

本例中设置了 Height 高度为 200 最终渲染代码为 max-height: 200px;,可通过切换每页显示数据行数体验不同效果

当设置了 OnAddAsync 或者 OnSaveAsync 回调委托方法时,如果未设置 EditTemplate 编辑模板时,组件会尝试自动生成表单维护 UI

Demo

通过设置 TItem 泛型约束的类实例 Foo 属性的 [Required] 等验证标签即可实现客户端验证

数据绑定类型为可为空类型时自动允许为空,如日期绑定列为 DateTime? 类型

数据绑定类型为数值类型时如,如数量绑定列为 int 类型,自动进行数值验证

表格呈现的有些数据列是计算得到的结果,此种类型的列是无法参与编辑的,通过设置 Editable=false 自动生成编辑 UI 时就不会生成此列编辑组件,如本示例中 Count 列在编辑弹窗中是不出现的

通过设置 Readonly=true 自动生成编辑 UI 会将此字段进行只读处理,新建时请对 Model 进行默认值赋值

通过设置 IsExtendButtonsInRowHeader=true 使扩展按钮在行前面显示

当设置列的 EditTemplate 时,组件自动生成表单维护 UI 时使用此模板作为呈现 UI

Demo

通过设置姓名列的 EditTemplate 自定义编辑时使用下拉框来选择姓名
本例中 Name 列为自定义组件 TableNameDrop,新建时默认为 请选择 ...

通过设置表格的 EditMode 属性,设置组件是弹窗编辑行数据还是行内编辑数据

Demo

EditMode 为枚举类型其值分别为:Popup EditForm InCell 其默认值为 Popup 弹窗编辑行数据

本例中设置参数 IsTracking=true 开启跟踪模式,此模式下编辑表单无需保存,直接编辑绑定行模型,特别适用与父子表录入

EditForm 模式示例

InCell 模式示例

通过设置表格的 UseInjectDataService 属性,使用数据服务进行对数据的增删改查

Demo

通过 UseInjectDataService="true" 开启使用数据服务进行增、删、改、查的数据库操作,而无需对以下回调委托进行赋值,优先级别为有回调方法优先调用回调方法,如无则调用注入服务进行数据操作

  • OnAddAsync
  • OnDeleteAsync
  • OnSaveAsync
  • OnQueryAsync
Startup 文件注入数据服务 实现原理与用法介绍
自定义数据服务
开启使用注入数据服务后,可通过设置 DataServices 参数对组件进行单独设置,如未设置内部使用注入服务提供的实例

services.AddTableDemoDataService();

通过设置表格的 DataService 属性,使用独立的数据服务进行对数据的增删改查

Demo

自定义数据服务

开启使用注入数据服务后,可通过设置 DataServices 参数对组件进行单独设置,如未设置内部使用注入服务提供的实例

services.AddTableDemoDataService();
@page "/tables/edit"

<h3>Table 表格</h3>

<h4>常用于单表维护,通过属性配置实现简单的增、删、改、查、排序、过滤、搜索等常用功能,通过 <code>Template</code> 的高级用法能实现非常复杂的业务需求功能</h4>

<Block Title="具有单表维护功能的表格" Introduction="设置 <code>ShowSearch</code> 显示查询组件" Name="ShowSearch">
    <p id="anchor1">本例中设置了 <code>Height</code> 高度为 <b>200</b> 最终渲染代码为 <code>max-height: 200px;</code>,可通过切换每页显示数据行数体验不同效果</p>
    <Table TItem="Foo" Height="200"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" IsExtendButtonsInRowHeader="true"
           OnQueryAsync="@OnQueryAsync" OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" />
            <TableColumn @bind-Field="@context.Address" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" ComponentType="@typeof(Switch)" />
        </TableColumns>
        <EditTemplate>
            <div class="row g-3 form-inline">
                <div class="col-12 col-sm-6">
                    <BootstrapInput @bind-Value="@context.Name" placeholder="不可为空,50字以内" maxlength="50" />
                </div>
                <div class="col-12 col-sm-6">
                    <BootstrapInput @bind-Value="@context.Address" placeholder="不可为空,50字以内" maxlength="50" />
                </div>
                <div class="col-12 col-sm-6">
                    <DateTimePicker @bind-Value="@context.DateTime" />
                </div>
                <div class="col-12 col-sm-6">
                    <Select @bind-Value="@context.Education" />
                </div>
                <div class="col-12 col-sm-6">
                    <BootstrapInputNumber @bind-Value="@context.Count" ShowButton="true" />
                </div>
                <div class="col-12 col-sm-6">
                    <Switch @bind-Value="@context.Complete" ShowInnerText="true" />
                </div>
            </div>
        </EditTemplate>
    </Table>
</Block>

<Block Title="自动生成单表维护功能的表格" Introduction="当设置了 <code>OnAddAsync</code> 或者 <code>OnSaveAsync</code> 回调委托方法时,如果未设置 <code>EditTemplate</code> 编辑模板时,组件会尝试自动生成表单维护 UI" Name="OnAddAsync">
    <p id="anchor2">通过设置 <code>TItem</code> 泛型约束的类实例 <code>Foo</code> 属性的 <code>[Required]</code> 等验证标签即可实现客户端验证</p>
    <p>数据绑定类型为可为空类型时自动允许为空,如日期绑定列为 <code>DateTime?</code> 类型</p>
    <p>数据绑定类型为数值类型时如,如数量绑定列为 <code>int</code> 类型,自动进行数值验证</p>
    <p>表格呈现的有些数据列是计算得到的结果,此种类型的列是无法参与编辑的,通过设置 <code>Editable=false</code> 自动生成编辑 UI 时就不会生成此列编辑组件,如本示例中 <code>Count</code> 列在编辑弹窗中是不出现的</p>
    <p>通过设置 <code>Readonly=true</code> 自动生成编辑 UI 会将此字段进行只读处理,新建时请对 <code>Model</code> 进行默认值赋值</p>
    <p>通过设置 <code>IsExtendButtonsInRowHeader=true</code> 使扩展按钮在行前面显示</p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" IsExtendButtonsInRowHeader="true"
           OnQueryAsync="@OnQueryAsync"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" />
            <TableColumn @bind-Field="@context.Address" Readonly="true" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" Editable="false" />
            <TableColumn @bind-Field="@context.Complete" ComponentType="@typeof(Switch)" />
        </TableColumns>
    </Table>
</Block>

<Block Title="自定义列编辑模板" Introduction="当设置列的 <code>EditTemplate</code> 时,组件自动生成表单维护 UI 时使用此模板作为呈现 UI" Name="EditTemplate">
    <p>
        <div>通过设置姓名列的 <code>EditTemplate</code> 自定义编辑时使用下拉框来选择姓名</div>
        <div>本例中 <code>Name</code> 列为自定义组件 <code>TableNameDrop</code>,新建时默认为 <code>请选择 ...</code></div>
    </p>

    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" IsFrontExtendButtons="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
           OnQueryAsync="@OnQueryAsync"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name">
                <EditTemplate Context="value">
                    <div class="col-12 col-sm-6">
                        <TablesNameDrop Model="@((Foo)value)" />
                    </div>
                </EditTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Address" Rows="3" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" ComponentType="@typeof(Switch)" />
        </TableColumns>
    </Table>
</Block>

<Block Title="设置编辑模式" Introduction="通过设置表格的 <code>EditMode</code> 属性,设置组件是弹窗编辑行数据还是行内编辑数据" Name="EditMode">
    <p id="anchor4"><code>EditMode</code> 为枚举类型其值分别为:<code>Popup</code> <code>EditForm</code> <code>InCell</code> 其默认值为 <code>Popup</code> 弹窗编辑行数据</p>
    <p>本例中设置参数 <code>IsTracking=true</code> 开启跟踪模式,此模式下编辑表单无需保存,直接编辑绑定行模型,特别适用与父子表录入</p>
    <p><code>EditForm</code> 模式示例</p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" IsTracking="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
           AddModalTitle="增加测试数据窗口" EditModalTitle="编辑测试数据窗口"
           OnQueryAsync="@OnQueryAsync" EditMode="EditMode.EditForm"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name">
                <EditTemplate Context="value">
                    <div class="col-12 col-sm-6">
                        <TablesNameDrop Model="@((Foo)value)" />
                    </div>
                </EditTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Address" Readonly="true" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" ComponentType="@typeof(Switch)" />
        </TableColumns>
    </Table>

    <p id="anchor5" class="mt-3"><code>InCell</code> 模式示例</p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
           AddModalTitle="增加测试数据窗口" EditModalTitle="编辑测试数据窗口"
           OnQueryAsync="@OnQueryAsync" EditMode="EditMode.InCell"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name">
                <EditTemplate Context="value">
                    <TablesNameDrop Model="@((Foo)value)" />
                </EditTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Address" Readonly="true" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" ComponentType="@typeof(Switch)" />
        </TableColumns>
    </Table>
</Block>

<Block Title="使用注入数据服务" Introduction="通过设置表格的 <code>UseInjectDataService</code> 属性,使用数据服务进行对数据的增删改查" Name="UseInjectDataService">
    <p id="anchor6">
        通过 <code>UseInjectDataService="true"</code> 开启使用数据服务进行增、删、改、查的数据库操作,而无需对以下回调委托进行赋值,优先级别为有回调方法优先调用回调方法,如无则调用注入服务进行数据操作
        <ul class="ul-demo">
            <li><code>OnAddAsync</code></li>
            <li><code>OnDeleteAsync</code></li>
            <li><code>OnSaveAsync</code></li>
            <li><code>OnQueryAsync</code></li>
        </ul>
        <div class="mb-3">Startup 文件注入数据服务 <a href="https://gitee.com/LongbowEnterprise/BootstrapBlazor/wikis/Table%20%E7%BB%84%E4%BB%B6%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1%E4%BB%8B%E7%BB%8D?sort_id=3207977" target="_blank">实现原理与用法介绍</a></div>
        <b>自定义数据服务</b>
        <div class="mt-1">开启使用注入数据服务后,可通过设置 <code>DataServices</code> 参数对组件进行单独设置,如未设置内部使用注入服务提供的实例</div>
    </p>
    <Pre class="no-highlight mt-3">services.AddTableDemoDataService();</Pre>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" UseInjectDataService="true">
        <TableColumns>
            <TableColumn @bind-Field="@context.Hobby" Data="@Hobbys" />
        </TableColumns>
    </Table>
</Block>

<Block Title="使用自定义数据服务" Introduction="通过设置表格的 <code>DataService</code> 属性,使用独立的数据服务进行对数据的增删改查" Name="DataService">
    <p id="anchor7">
        <b>自定义数据服务</b>
        <div class="mt-1">开启使用注入数据服务后,可通过设置 <code>DataServices</code> 参数对组件进行单独设置,如未设置内部使用注入服务提供的实例</div>
    </p>
    <Pre class="no-highlight mt-3">services.AddTableDemoDataService();</Pre>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource" DataService="@CustomerDataService"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" UseInjectDataService="true">
        <TableColumns>
            <TableColumn @bind-Field="@context.Hobby" Data="@Hobbys" />
        </TableColumns>
    </Table>
</Block>
// 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.Pages.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;

namespace BootstrapBlazor.Shared.Pages.Table
{
    /// <summary>
    /// 表单编辑功能示例
    /// </summary>
    public partial class TablesEdit
    {
        private static readonly ConcurrentDictionary<Type, Func<IEnumerable<Foo>, string, SortOrder, IEnumerable<Foo>>> SortLambdaCache = new();

        [Inject]
        [NotNull]
        private IStringLocalizer<Foo>? Localizer { get; set; }

        private static IEnumerable<int> PageItemsSource => new int[] { 4, 10, 20 };

        [NotNull]
        private IEnumerable<SelectedItem>? Hobbys { get; set; }

        [NotNull]
        private List<Foo>? Items { get; set; }

        /// <summary>
        /// OnInitialized 方法
        /// </summary>
        protected override void OnInitialized()
        {
            base.OnInitialized();

            Hobbys = Foo.GenerateHobbys(Localizer);
            Items = Foo.GenerateFoo(Localizer);

            CustomerDataService = new FooDataService<Foo>(Localizer);
        }

        private static Task<Foo> OnAddAsync() => Task.FromResult(new Foo() { DateTime = DateTime.Now, Address = $"自定义地址  {DateTime.Now.Second}" });

        private Task<bool> OnSaveAsync(Foo item, ItemChangedType changedType)
        {
            // 增加数据演示代码
            if (changedType == ItemChangedType.Add)
            {
                item.Id = Items.Max(i => i.Id) + 1;
                Items.Add(item);
            }
            else
            {
                var oldItem = Items.FirstOrDefault(i => i.Id == item.Id);
                if (oldItem != null)
                {
                    oldItem.Name = item.Name;
                    oldItem.Address = item.Address;
                    oldItem.DateTime = item.DateTime;
                    oldItem.Count = item.Count;
                    oldItem.Complete = item.Complete;
                    oldItem.Education = item.Education;
                }
            }
            return Task.FromResult(true);
        }

        private Task<bool> OnDeleteAsync(IEnumerable<Foo> items)
        {
            items.ToList().ForEach(i => Items.Remove(i));
            return Task.FromResult(true);
        }

        private Task<QueryData<Foo>> OnQueryAsync(QueryPageOptions options)
        {
            IEnumerable<Foo> items = Items;

            // 过滤
            var isFiltered = false;
            if (options.Filters.Any())
            {
                items = items.Where(options.Filters.GetFilterFunc<Foo>());
                isFiltered = true;
            }

            // 排序
            var isSorted = false;
            if (!string.IsNullOrEmpty(options.SortName))
            {
                var invoker = SortLambdaCache.GetOrAdd(typeof(Foo), key => LambdaExtensions.GetSortLambda<Foo>().Compile());
                items = invoker(items, options.SortName, options.SortOrder);
                isSorted = true;
            }

            // 设置记录总数
            var total = items.Count();

            // 内存分页
            items = items.Skip((options.PageIndex - 1) * options.PageItems).Take(options.PageItems).ToList();

            return Task.FromResult(new QueryData<Foo>()
            {
                Items = items,
                TotalCount = total,
                IsSorted = isSorted,
                IsFiltered = isFiltered,
                IsSearch = true
            });
        }

        [NotNull]
        private IDataService<Foo>? CustomerDataService { get; set; }

        private class FooDataService<TModel> : TableDemoDataService<TModel> where TModel : class, new()
        {
            public FooDataService(IStringLocalizer<TModel> localizer) : base(localizer)
            {

            }
        }
    }
}

B 站相关视频链接

[传送门]

交流群

QQ Group:BootstrapAdmin & Blazor 795206915( Full) 675147445 Welcome to join the group discussion
Themes
Bootstrap
Ant Design (完善中)
Bluma (完善中)
LayUI (完善中)
Motronic (已集成)
An error has occurred. This application may no longer respond until reloaded. Reload