Enterprise-level component library based on Bootstrap and Blazor

star nuget master download license repo commit

Table 表格

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

具有搜索功能的表格

设置 ShowSearch 显示查询组件,通过设置 SearchTemplate 模板自定义搜索 UI

Demo
Loading...

自动生成搜索功能的表格

当设置了 ShowSearch 时,如果未设置 SearchTemplate 编辑模板时,组件会尝试自动生成搜索条件 UI

Demo

列信息绑定时通过设置 Searchable 属性,设置搜索条件自动构建 UI

if (options.Searchs.Any())
{
    // 逻辑关系使用 FilterLogic.Or
    items = items.Where(options.Searchs.GetFilterFunc<Foo>(FilterLogic.Or));
}
Loading...

自定义列搜索模板

当设置了 SearchTemplate 时,组件自动生成搜索 UI 时使用此模板作为呈现 UI

Demo

通过设置姓名列的 SearchTemplate 自定义编辑时使用下拉框来选择姓名

由于是搜索条件,本例中姓名搜索列下拉框增加了 请选择... 项

生成列搜索模板是查找顺序为 SearchTemplate -> AutoGenerate 优先查找是否设置了搜索模板,然后根据绑定字段类型自动生成

Loading...
@page "/tables/search"

<h3>Table 表格</h3>

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

<Block Title="具有搜索功能的表格" Introduction="设置 <code>ShowSearch</code> 显示查询组件,通过设置 <code>SearchTemplate</code> 模板自定义搜索 UI">
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true"
           ShowToolbar="true" ShowSearch="true" IsMultipleSelect="true" ShowExtendButtons="true"
           AddModalTitle="增加测试数据窗口" EditModalTitle="编辑测试数据窗口"
           SearchModel="@SearchModel"
           OnQueryAsync="@OnQueryAsync" OnResetSearchAsync="@OnResetSearchAsync"
           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" />
        </TableColumns>
        <SearchTemplate>
            <div class="form-inline">
                <div class="row">
                    <div class="form-group col-12 col-sm-6">
                        <BootstrapInput @bind-Value="@context.Name" placeholder="不可为空,50字以内" maxlength="50" ShowLabel="true" DisplayText="@Localizer[nameof(context.Name)]" />
                    </div>
                    <div class="form-group col-12 col-sm-6">
                        <BootstrapInput @bind-Value="@context.Address" placeholder="不可为空,50字以内" maxlength="50" ShowLabel="true" DisplayText="@Localizer[nameof(context.Address)]" />
                    </div>
                </div>
            </div>
        </SearchTemplate>
    </Table>
</Block>

<Block Title="自动生成搜索功能的表格" Introduction="当设置了 <code>ShowSearch</code> 时,如果未设置 <code>SearchTemplate</code> 编辑模板时,组件会尝试自动生成搜索条件 UI">
    <p>列信息绑定时通过设置 <code>Searchable</code> 属性,设置搜索条件自动构建 UI</p>
    <Tips>
        <p>自动构建搜索弹窗时,由于各列设置 <code>Searchable</code> 此时组件会通过 <code>SearchText</code> 与设置 <code>Searchable</code> 值为 <code>true</code> 的各列自动构建搜索拉姆达表达式,通过 <code>QueryPageOptions</code> 的属性 <code>Searchs</code> 获得</p>

    </Tips>
    <Pre>if (options.Searchs.Any())
{
    // 逻辑关系使用 FilterLogic.Or
    items = items.Where(options.Searchs.GetFilterFunc&lt;Foo&gt;(FilterLogic.Or));
}</Pre>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" SearchModel="@SearchModel" ShowSearch="true"
           ShowToolbar="true" IsMultipleSelect="true" ShowExtendButtons="true"
           AddModalTitle="增加测试数据窗口" EditModalTitle="编辑测试数据窗口"
           OnQueryAsync="@OnQueryAsync"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" Searchable="true" />
            <TableColumn @bind-Field="@context.Address" Searchable="true" />
            <TableColumn @bind-Field="@context.Education" />
        </TableColumns>
    </Table>
</Block>

<Block Title="自定义列搜索模板" Introduction="当设置了 <code>SearchTemplate</code> 时,组件自动生成搜索 UI 时使用此模板作为呈现 UI">
    <p>通过设置姓名列的 <code>SearchTemplate</code> 自定义编辑时使用下拉框来选择姓名</p>
    <p>由于是搜索条件,本例中姓名搜索列下拉框增加了 请选择... 项</p>
    <p>生成列搜索模板是查找顺序为 SearchTemplate -> AutoGenerate 优先查找是否设置了搜索模板,然后根据绑定字段类型自动生成</p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" SearchModel="@SearchModel" ShowSearch="true"
           ShowToolbar="true" IsMultipleSelect="true" ShowExtendButtons="true"
           AddModalTitle="增加测试数据窗口" EditModalTitle="编辑测试数据窗口"
           OnQueryAsync="@OnQueryAsync"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" Searchable="true">
                <SearchTemplate Context="value">
                    @{
                        var model = value as Foo;
                        if (model != null)
                        {
                            var items = new List<SelectedItem>()
                            {
                                new SelectedItem { Text = "请选择 ...", Value = "" },
                                new SelectedItem { Text = "姓名1", Value = "姓名1" },
                                new SelectedItem { Text = "姓名2", Value = "姓名2" },
                            };
                            <div class="form-group col-12 col-sm-6">
                                <Select Items="items" @bind-Value="@model.Name" ShowLabel="true" DisplayText="姓名" />
                            </div>
                        }
                    }
                </SearchTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Address" Searchable="true" />
            <TableColumn @bind-Field="@context.Education" />
        </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.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;

namespace BootstrapBlazor.Shared.Pages.Table
{
    /// <summary>
    /// Table 组件搜索示例代码
    /// </summary>
    public sealed partial class TablesSearch
    {
        private static readonly ConcurrentDictionary<Type, Func<IEnumerable<Foo>, string, SortOrder, IEnumerable<Foo>>> SortLambdaCache = new();

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

        private Foo SearchModel { get; set; } = new Foo();

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

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

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

            Items = FooExtensions.GenerateFoo(Localizer);
        }

        private static Task<Foo> OnAddAsync() => Task.FromResult(new Foo() { DateTime = DateTime.Now });

        private Task<bool> OnSaveAsync(Foo item)
        {
            // 增加数据演示代码
            if (item.Id == 0)
            {
                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 static Task OnResetSearchAsync(Foo item)
        {
            item.Name = "";
            item.Address = "";
            return Task.CompletedTask;
        }

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

            // 处理高级搜索
            if (!string.IsNullOrEmpty(SearchModel.Name))
            {
                items = items.Where(item => item.Name?.Contains(SearchModel.Name, StringComparison.OrdinalIgnoreCase) ?? false);
            }

            if (!string.IsNullOrEmpty(SearchModel.Address))
            {
                items = items.Where(item => item.Address?.Contains(SearchModel.Address, StringComparison.OrdinalIgnoreCase) ?? false);
            }

            // 处理 Searchable=true 列与 SeachText 模糊搜索
            if (options.Searchs.Any())
            {
                items = items.Where(options.Searchs.GetFilterFunc<Foo>(FilterLogic.Or));
            }
            else
            {
                // 处理 SearchText 模糊搜索
                if (!string.IsNullOrEmpty(options.SearchText))
                {
                    items = items.Where(item => (item.Name?.Contains(options.SearchText) ?? false)
                                 || (item.Address?.Contains(options.SearchText) ?? false));
                }
            }

            // 过滤
            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 = !string.IsNullOrEmpty(SearchModel.Name) || !string.IsNullOrEmpty(SearchModel.Address)
            });
        }
    }
}

B 站相关视频链接

[传送门]

交流群

QQ群: BootstrapAdmin & Blazor(795206915)欢迎加群讨论
An error has occurred. This application may no longer respond until reloaded. Reload