Enterprise-level component library based on Bootstrap and Blazor

star nuget master download license repo commit

Table 表格

常用于大数据单表维护

通过设置表格的 IsExcel 属性,使组件呈现为类似 Excel

Demo

Excel 模式下绑定是 动态类型 时,无法使用 TableColumn 对列属性进行设置,本例中使用 DynamicContext 实例对象 DataTableDynamicContext 构造函数进行设置

Loading...

设置列 Items 值,将枚举类型渲染成 Select 组件

Demo
DataTableDynamicContext = new(UserData, (context, col) =>
{
    if (col.GetFieldName() == nameof(Foo.Education))
    {
        col.ComponentType = typeof(Select<string>);
        col.Items = typeof(EnumEducation).ToSelectList(new SelectedItem("", "未设置"));
    }
});
通过上面代码将 Education 列使用 Select 组件渲染

本示例用于测试 Excel 模式下键盘支持

Demo

目前支持 Tab

Loading...

使用 DatTable 为数据源时,需要按照下面的步骤进行设置

1. 设置数据源上下文
设置 Table 组件的 TItem 属性值为 DynamicObject
设置 Table 组件的 DynamicContext 属性值为 DataTableDynamicContext 实例

protected override void OnInitialized()
{
    base.OnInitialized();

    DataTableDynamicContext = new(UserData, (context, col) =>
    {
        // 设置 Enum 类型渲染成 Select
        if (col.GetFieldName() == nameof(Foo.Education))
        {
            col.ComponentType = typeof(Select<string>);

            // 将枚举转化为 List
            col.Items = typeof(EnumEducation).ToSelectList(new SelectedItem("", Localizer["NullItemText"].Value));
        }
    });
}

2. 处理 DataRow 变化逻辑
设置 OnChanged 回调委托函数处理 新建/删除 逻辑

protected override void OnInitialized()
{
    DataTableDynamicContext.OnChanged = args =>
    {
        // 输出日志信息
        Trace.Log($"集合值变化通知 行: {args.Items.Count()} - 类型: {args.ChangedType}");
        return Task.CompletedTask;
    };
}

3. 处理 DataCell 变化逻辑
设置 OnValueChanged 回调委托函数处理单元格 更新 逻辑

protected override void OnInitialized()
{
    // 获得内置 OnValueChanged 回调
    var method = DataTableDynamicContext.OnValueChanged;

    DataTableDynamicContext.OnValueChanged = async (model, col, val) =>
    {
        // 调用内部提供的方法
        if (method != null)
        {
            // 内部方法会更新原始数据源 DataTable
            await method(model, col, val);
        }

        // 输出日志信息
        Trace.Log($"单元格变化通知 列: {col.GetFieldName()} - 值: {val?.ToString()}");
    };
}
@page "/tables/excel"

<h3>Table 表格</h3>

<h4>常用于大数据单表维护</h4>

<Block Title="绑定集合" Introduction="通过 <code>OnQueryAsync</code> 回调获得数据集合" Name="OnQuery">
    <Table TItem="Foo" ShowColumnList="true"
           IsExcel="true" IsPagination="true" PageItemsSource="@PageItemsSource"
           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" Align="Alignment.Right" />
            <TableColumn @bind-Field="@context.Complete" Align="Alignment.Center" />
        </TableColumns>
    </Table>

    <BlockLogger @ref="Trace" class="mt-3" />
</Block>

<p class="mt-3">使用 <code>List&lt;TItem&gt;</code> 泛型集合作为数据源时,需要按照下面的步骤进行设置</p>

<p>
    <div class="code-label">1. 设置数据源</div>
    <div class="mt-2">设置 <code>Table</code> 组件的 <code>Items</code> 属性或者 <code>OnQueryAsync</code> 回调委托方法</div>
</p>

<Pre>protected override void OnInitialized()
{
    base.OnInitialized();

    Items = Foo.GenerateFoo(Localizer);
}</Pre>

<p>
    <div class="code-label">2. 处理新建逻辑</div>
    <div class="mt-2">设置 <code>OnAddAsync</code> 回调委托函数处理 <b>新建</b> 逻辑</div>
</p>

<Pre>private Task&lt;Foo&gt; OnAddAsync()
{
    // 此处代码为示例代码
    var foo = new Foo() { DateTime = DateTime.Now, Address = $"自定义地址  {DateTime.Now.Second}" };
    Items.Insert(0, foo);

    // 输出日志信息
    Trace.Log($"集合值变化通知 列: {Items.Count} - 类型: Add");
    return Task.FromResult(foo);
}</Pre>

<p>
    <div class="code-label">3. 处理删除逻辑</div>
    <div class="mt-2">设置 <code>OnDeleteAsync</code> 回调委托函数处理 <b>删除</b> 逻辑</div>
</p>

<Pre>private Task&lt;bool&gt; OnDeleteAsync(IEnumerable&lt;Foo&gt; items)
{
    // 此处代码为示例代码
    Items.RemoveAll(i => items.Contains(i));

    // 输出日志信息
    Trace.Log($"集合值变化通知 列: {Items.Count} - 类型: Delete");
    return Task.FromResult(true);
}</Pre>

<p>
    <div class="code-label">4. 处理更新逻辑</div>
    <div class="mt-2">设置 <code>OnSaveAsync</code> 回调委托函数处理单元格 <b>更新</b> 逻辑</div>
    <div class="mt-2">组件内部所有单元格编辑更新后会自动触发 <code>OnSaveAsync</code> 回调委托,参数是当前更新模型 <code>TItem</code></div>
</p>

<Pre>private Task&lt;bool&gt; OnDeleteAsync(Foo item, ItemChangedType changedType)
{
    // 此处代码为示例代码
    Trace.Log($"单元格变化通知 类: Foo - 值: 单元格");
    return Task.FromResult(true);
}</Pre>

<Block Title="通过编辑模板单独控制单元格渲染方式" Introduction="高级用法" Name="CellRender">
    <ul class="ul-demo">
        <li><code>IsFixedHeader</code> 固定表头 高度设定为 <code>Height="500px"</code></li>
        <li><code>Name</code> 不可编辑显示头像</li>
    </ul>
    <Table TItem="Foo" Items="@Items" IsBordered="true" IsExcel="true" IsFixedHeader="true" Height="500">
        <TableColumns>
            <TableColumn @bind-Field="@context.Name" Width="200">
                <EditTemplate Context="value">
                    @{
                        var row = (Foo)value;
                    }
                    <div class="d-flex disabled">
                        <div>
                            <img src="@Foo.GetAvatarUrl(row.Id)" class="bb-avatar" />
                        </div>
                        <div class="ps-2">
                            <div>@row.Name</div>
                            <div class="bb-sub">@GetTitle(row.Id)</div>
                        </div>
                    </div>
                </EditTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Address" />
            <TableColumn @bind-Field="@context.Education" Width="160" />
            <TableColumn @bind-Field="@context.Count" Width="160">
                <EditTemplate Context="value">
                    @{
                        var row = (Foo)value;
                    }
                    <div class="disabled">
                        <div>@row.Count %</div>
                        <Progress Value="@row.Count" Color="@Foo.GetProgressColor(row.Count)"></Progress>
                    </div>
                </EditTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Complete" Align="Alignment.Center" Width="80" />
        </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.Localization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;

namespace BootstrapBlazor.Shared.Pages.Table
{
    /// <summary>
    /// 
    /// </summary>
    partial class TablesExcel
    {
        [Inject]
        [NotNull]
        private IStringLocalizer<Foo>? Localizer { get; set; }

        [Inject]
        [NotNull]
        private IStringLocalizer<Tables>? TablesLocalizer { get; set; }

        [NotNull]
        private BlockLogger? Trace { get; set; }

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

            Items = Foo.GenerateFoo(Localizer);
        }

        // 绑定数据源代码
        private static IEnumerable<int> PageItemsSource => new int[] { 10, 20, 40 };

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

        private static readonly ConcurrentDictionary<Type, Func<IEnumerable<Foo>, string, SortOrder, IEnumerable<Foo>>> SortLambdaCache = new();

        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
            });
        }

        private Task<Foo> OnAddAsync()
        {
            // Excel 模式下新建要求更改数据源
            var foo = new Foo() { DateTime = DateTime.Now, Address = $"自定义地址  {DateTime.Now.Second}" };
            Items.Insert(0, foo);

            // 输出日志信息
            Trace.Log($"集合值变化通知 列: {Items.Count} - 类型: Add");
            return Task.FromResult(foo);
        }

        private Task<bool> OnSaveAsync(Foo item, ItemChangedType changedType)
        {
            // 对象已经更新
            // 输出日志信息
            Trace.Log($"单元格变化通知 类: Foo - 值: 单元格");
            return Task.FromResult(true);
        }

        private Task<bool> OnDeleteAsync(IEnumerable<Foo> items)
        {
            Items.RemoveAll(i => items.Contains(i));

            // 输出日志信息
            Trace.Log($"集合值变化通知 列: {Items.Count} - 类型: Delete");
            return Task.FromResult(true);
        }

        private ConcurrentDictionary<int, string> TitleCache { get; } = new();

        private string GetTitle(int id) => TitleCache.GetOrAdd(id, key => Foo.GetTitle());
    }
}

B 站相关视频链接

[传送门]

交流群

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