Blazor 自定义可重用基础组件之 带标头排序的Table

19 篇文章 1 订阅

实现点击标头按所在列值进行排序,是一个非常有用的功能,其他的UI一般搞得非常复杂,添加标志图标什么的,使得本就不宽裕的表格更加拥挤。我的思路是,点击所在列的标头部位,传递标头值,然后根据标头值来改变查询语句。因为并不是所有列都需要排序,需要有一个特征字符。

以下是Table.razor

@typeparam T
<style>
    th {
        text-align: center;
        font-weight: 500;
        font-size: 16px;
        white-space: nowrap;
    }

    td {
        font-size: 14.66px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        line-height: 25px;
    }
</style>
<div>
    @ToolBarContent
</div>
<div>
    <table class="table table-bordered table-hover table-sm table-striped">
        <thead>
            <tr >
                @foreach (var item in HeaderTitles)
                {
                    if (IsSortabledColonum((string)item))
                    {
                        <th @onclick="()=>HeaderClick((string)item)" style="color:blue;cursor:pointer;">@HeaderTitle((string)item)</th>
                    }
                    else
                    {
                        <th>@item</th>
                    }
                }
            </tr>
        </thead>
        <tbody>
            @if (PageData.HasData)
            {
                foreach (var item in PageData)
                {
                    <tr @onclick="()=>RowClick(item)" >
                        @RowContent(item)
                    </tr>
                }
            }
            else
            {
                <tr>
                    <td colspan="@columnsCount" class="text-center">暂无数据</td>
                </tr>
            }
        </tbody>
    </table>
    <div class="row col-12 justify-content-end">
        <p style="width:auto;">
            <label class="form-label">
                设置行数:
                <select class="form-control-sm mt-0" @onchange="PageSizeChanged">
                    <option value="10">10</option>
                    <option value="15" selected>15</option>
                    <option value="20">20</option>
                    <option value="25">25</option>
                    <option value="30">30</option>
                </select>
            </label>
        </p>

        <button class="btn btn-primary btn-sm" disabled="@PageData.NoPreviousPage" @onclick="()=>GotoPage(1)" style="width:80px;height:30px;">首页</button>
        <button class="btn btn-primary btn-sm" disabled="@PageData.NoPreviousPage" @onclick="PreviousPage" style="width:80px;height:30px;">上一页</button>
        <button class="btn btn-primary btn-sm" disabled="@PageData.NoNextPage" @onclick="NextPage" style="width:80px;height:30px;">下一页</button>
        <button class="btn btn-primary btn-sm" disabled="@PageData.NoNextPage" @onclick="()=>GotoPage(PageData.TotalPages)" style="width:60px;height:30px;">尾页</button>
        <input type="number" style="width:60px;height:30px;" @bind-value="pageNum" />
        <button class="btn btn-primary btn-sm" disabled="@PageData.NoGoto" @onclick="()=>GotoPage(pageNum)" style="width:60px;height:30px;">跳转</button>
        <p class="mx-4" style="width:130px;height:30px;">总页数:@PageData.TotalPages</p>
        <p style="width:130px;height:30px;">当前页:@pageIndex</p>
        <p style="width:130px;height:30px;">总条数:@PageData.ItemCount</p>
    </div>
</div>

Table.razor.cs

    public partial class Table<T> : ComponentBase, IDisposable where T : class
    {
        private int pageIndex = 1;
        private int pageSize = 15;
        private int pageNum;
        private IQueryable<T>? query;
        private int columnsCount;//设定列数

        private PaginatedList<T> PageData { get; set; } = new(new List<T>(), 0, 1, 1);

        [Parameter]
        public RenderFragment? ToolBarContent { get; set; }
        [Parameter]
        public required Array HeaderTitles { get; set; }
        [Parameter]
        public required RenderFragment<T> RowContent { get; set; }
        [Parameter]
        public RenderFragment<RenderFragment>? RowTemplate { get; set; }
        [Parameter]
        public EventCallback<T> OnRowClick { get; set; }
        [Parameter]
        public EventCallback<string> OnHeaderClick { get; set; }
        public async void RowClick(T item)
        {
            if (OnRowClick.HasDelegate)
                await OnRowClick.InvokeAsync(item);
        }
        public async void HeaderClick(string title)
        {
            if (OnHeaderClick.HasDelegate)
                await OnHeaderClick.InvokeAsync(title[..^1]);
        }
        protected override void OnParametersSet()
        {
            base.OnParametersSet();
            columnsCount = HeaderTitles.Length;
        }
        public int PageSize
        {
            get => pageSize;

            set
            {
                if (value != pageSize)
                {
                    pageSize = value;
                    OnChange();
                }
            }
        }

        public IQueryable<T> Query
        {
            get => query ?? (new List<T>()).AsQueryable();
            set
            {
                if (value != query)
                {
                    query = value;
                    OnChange();
                }
            }
        }
        private void OnChange()
        {
            PageData = PaginatedList<T>.Create(Query, pageIndex, PageSize);
            StateHasChanged();
        }
        private void PreviousPage()
        {
            pageIndex--;
            OnChange();
        }
        private void NextPage()
        {
            pageIndex++;
            OnChange();
        }
        private void GotoPage(int pageNum)
        {
            if (pageNum > 0 && pageNum != pageIndex && pageNum <= PageData.TotalPages)
            {
                pageIndex = pageNum;
                OnChange();
            }
        }
        private void PageSizeChanged(ChangeEventArgs e)
        {
            if (e.Value != null)
            {
                PageSize = int.Parse((string)e.Value);
            }
        }
        private string HeaderTitle(string title)
        {
            if (title.EndsWith('|'))
            {
                return title[..^1];
            }
            return title;
        }
        private bool IsSortabledColonum(string title)
        {
            return title.EndsWith('|'); //点击标头可以排序等操作。
        }

        protected virtual void Dispose(bool disposing)
        {
        }

        void IDisposable.Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }

还是那个天气表格:

@page "/fetchdata"
@using MySoft.Data
@inject WeatherForecastService ForecastService

<PageTitle>Weather forecast</PageTitle>

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

<Table @ref="myTable" HeaderTitles="headerTitles" T="WeatherForecast" OnHeaderClick="OnSort">
    <RowContent>
        <td>@context.Date.ToShortDateString()</td>
        <td>@context.TemperatureC</td>
        <td>@context.TemperatureF</td>
        <td>@context.Summary</td>
    </RowContent>
</Table>

@code {
    private WeatherForecast[] forecasts = new WeatherForecast[] { };
    private YtTable<WeatherForecast>? myTable;
    private Array headerTitles = new string[]
    {
        "Date|","Temp. (C)","Temp. (F)","Summary|"
    };
    private bool dateSort, summarySort;
    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
    }
    private IQueryable<WeatherForecast> Query
    {
        get
        {
            if (myTable != null)
            {
                return myTable.Query;
            }
            return (new List<WeatherForecast>()).AsQueryable();
        }
        set
        {
            if (myTable != null)
            {
                myTable.Query = value;
            }
        }
    }
    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);
        if (firstRender)
        {
            Query = forecasts.AsQueryable();
        }
    }
    private void OnSort(string title)
    {
        switch (title)
        {
            case "Date":
                sortOnDate();
                break;
            case "Summary":
                sortOnSummary();
                break;
            default:
                break;
        }
    }
    private void sortOnDate()
    {
        dateSort = !dateSort;
        if (dateSort)
        {
            Query = Query.OrderBy(o => o.Date);
        }
        else
        {
            Query = Query.OrderByDescending(o => o.Date);
        }
    }
    private void sortOnSummary()
    {
        summarySort = !summarySort;
        if (summarySort)
        {
            Query = Query.OrderBy(o => o.Summary);
        }
        else
        {
            Query = Query.OrderByDescending(o => o.Summary);
        }
    }
}

效果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落单枫叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值