Compare commits
2 Commits
ae9b448a64
...
e2ca65ed98
| Author | SHA1 | Date | |
|---|---|---|---|
| e2ca65ed98 | |||
| bed1ad7c66 |
@@ -64,7 +64,7 @@
|
|||||||
<MudText Class="mt-2 mud-text-secondary">در حال بارگذاری...</MudText>
|
<MudText Class="mt-2 mud-text-secondary">در حال بارگذاری...</MudText>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
}
|
}
|
||||||
else if (_result.Products.Count == 0)
|
else if (_products.Count == 0)
|
||||||
{
|
{
|
||||||
<MudAlert Severity="Severity.Info" Class="my-4">محصولی یافت نشد.</MudAlert>
|
<MudAlert Severity="Severity.Info" Class="my-4">محصولی یافت نشد.</MudAlert>
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
{
|
{
|
||||||
<!-- Products Grid -->
|
<!-- Products Grid -->
|
||||||
<MudGrid Spacing="1">
|
<MudGrid Spacing="1">
|
||||||
@foreach (var p in _result.Products)
|
@foreach (var p in _products)
|
||||||
{
|
{
|
||||||
<MudItem xs="6" sm="6" md="3"
|
<MudItem xs="6" sm="6" md="3"
|
||||||
onclick="@(() => NavigateToProduct(p.Id))">
|
onclick="@(() => NavigateToProduct(p.Id))">
|
||||||
@@ -119,15 +119,29 @@
|
|||||||
}
|
}
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
|
|
||||||
<!-- Pagination -->
|
@* Lazy Load — بارگذاری بیشتر *@
|
||||||
@if (_result.TotalPages > 1)
|
@if (_loadingMore)
|
||||||
{
|
{
|
||||||
<div class="d-flex justify-center mt-4">
|
<MudStack AlignItems="AlignItems.Center" Class="py-4">
|
||||||
<MudPagination Count="@_result.TotalPages" Selected="@_currentPage"
|
<MudProgressCircular Color="Color.Primary" Size="Size.Small" Indeterminate="true"/>
|
||||||
SelectedChanged="OnPageChanged"
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">بارگذاری بیشتر...</MudText>
|
||||||
Color="Color.Primary" Variant="Variant.Filled"
|
</MudStack>
|
||||||
BoundaryCount="1" MiddleCount="3" />
|
}
|
||||||
|
else if (_hasMore)
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-center py-4">
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.ExpandMore"
|
||||||
|
OnClick="LoadMore">
|
||||||
|
نمایش محصولات بیشتر
|
||||||
|
</MudButton>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
else if (_products.Count > 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Align="Align.Center" Class="py-4 mud-text-secondary">
|
||||||
|
همه @(_products.Count) محصول نمایش داده شد
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
private long? _selectedCategoryId;
|
private long? _selectedCategoryId;
|
||||||
private int _currentPage = 1;
|
private int _currentPage = 1;
|
||||||
private bool _loading;
|
private bool _loading;
|
||||||
|
private bool _loadingMore;
|
||||||
|
private bool _hasMore = true;
|
||||||
|
private int _totalCount;
|
||||||
private const int PageSize = 12;
|
private const int PageSize = 12;
|
||||||
|
|
||||||
private DiscountProductListResult _result = new(new(), 0, 0, 1);
|
private List<DiscountProductCard> _products = new();
|
||||||
private List<DiscountCategoryNode> _categories = new();
|
private List<DiscountCategoryNode> _categories = new();
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
@@ -27,45 +30,66 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
var productsTask = ProductService.GetProductsAsync(page: 1, pageSize: PageSize);
|
var productsTask = ProductService.GetProductsAsync(page: 1, pageSize: PageSize);
|
||||||
await Task.WhenAll(categoriesTask, productsTask);
|
await Task.WhenAll(categoriesTask, productsTask);
|
||||||
_categories = categoriesTask.Result;
|
_categories = categoriesTask.Result;
|
||||||
_result = productsTask.Result;
|
var result = productsTask.Result;
|
||||||
|
_products = result.Products;
|
||||||
|
_totalCount = result.TotalCount;
|
||||||
|
_hasMore = result.CurrentPage < result.TotalPages;
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadProductsAsync()
|
private async Task LoadInitial()
|
||||||
{
|
{
|
||||||
_loading = true;
|
_loading = true;
|
||||||
|
_currentPage = 1;
|
||||||
|
_products.Clear();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
_result = await ProductService.GetProductsAsync(
|
|
||||||
|
var result = await ProductService.GetProductsAsync(
|
||||||
|
page: 1,
|
||||||
|
pageSize: PageSize,
|
||||||
|
search: string.IsNullOrWhiteSpace(_search) ? null : _search,
|
||||||
|
categoryId: _selectedCategoryId);
|
||||||
|
_products = result.Products;
|
||||||
|
_totalCount = result.TotalCount;
|
||||||
|
_hasMore = result.CurrentPage < result.TotalPages;
|
||||||
|
_loading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadMore()
|
||||||
|
{
|
||||||
|
if (_loadingMore || !_hasMore) return;
|
||||||
|
|
||||||
|
_loadingMore = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
_currentPage++;
|
||||||
|
var result = await ProductService.GetProductsAsync(
|
||||||
page: _currentPage,
|
page: _currentPage,
|
||||||
pageSize: PageSize,
|
pageSize: PageSize,
|
||||||
search: string.IsNullOrWhiteSpace(_search) ? null : _search,
|
search: string.IsNullOrWhiteSpace(_search) ? null : _search,
|
||||||
categoryId: _selectedCategoryId);
|
categoryId: _selectedCategoryId);
|
||||||
_loading = false;
|
_products.AddRange(result.Products);
|
||||||
|
_hasMore = result.CurrentPage < result.TotalPages;
|
||||||
|
|
||||||
|
_loadingMore = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SearchProducts() => await LoadProductsAsync();
|
private async Task SearchProducts() => await LoadInitial();
|
||||||
|
|
||||||
private async Task OnSearchKeyUp(KeyboardEventArgs e)
|
private async Task OnSearchKeyUp(KeyboardEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == "Enter")
|
if (e.Key == "Enter")
|
||||||
{
|
{
|
||||||
_currentPage = 1;
|
await LoadInitial();
|
||||||
await LoadProductsAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnCategoryChanged(long? value)
|
private async Task OnCategoryChanged(long? value)
|
||||||
{
|
{
|
||||||
_selectedCategoryId = value;
|
_selectedCategoryId = value;
|
||||||
_currentPage = 1;
|
await LoadInitial();
|
||||||
await LoadProductsAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnPageChanged(int page)
|
|
||||||
{
|
|
||||||
_currentPage = page;
|
|
||||||
await LoadProductsAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddToCart(DiscountProductCard p)
|
private async Task AddToCart(DiscountProductCard p)
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
{
|
{
|
||||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Inventory2" Size="Size.Small" Class="me-1"/>
|
<MudIcon Icon="@Icons.Material.Filled.Inventory2" Size="Size.Small" Class="me-1"/>
|
||||||
@(_products.Count) محصول
|
@(_totalCount) محصول
|
||||||
</MudText>
|
</MudText>
|
||||||
}
|
}
|
||||||
</MudItem>
|
</MudItem>
|
||||||
@@ -159,5 +159,30 @@
|
|||||||
</MudItem>
|
</MudItem>
|
||||||
}
|
}
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
|
|
||||||
|
@* Lazy Load — بارگذاری بیشتر *@
|
||||||
|
@if (_loadingMore)
|
||||||
|
{
|
||||||
|
<MudStack AlignItems="AlignItems.Center" Class="py-4">
|
||||||
|
<MudProgressCircular Color="Color.Primary" Size="Size.Small" Indeterminate="true"/>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">بارگذاری بیشتر...</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (_hasMore)
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-center py-4">
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.ExpandMore"
|
||||||
|
OnClick="LoadMore">
|
||||||
|
نمایش محصولات بیشتر
|
||||||
|
</MudButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (_products.Count > 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Align="Align.Center" Class="py-4 mud-text-secondary">
|
||||||
|
همه @(_products.Count) محصول نمایش داده شد
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
|
|
||||||
private string _query = string.Empty;
|
private string _query = string.Empty;
|
||||||
private bool _loading;
|
private bool _loading;
|
||||||
|
private bool _loadingMore;
|
||||||
|
private bool _hasMore = true;
|
||||||
|
private int _currentPage = 1;
|
||||||
|
private int _totalCount;
|
||||||
|
private const int PageSize = 12;
|
||||||
private List<Product> _products = new();
|
private List<Product> _products = new();
|
||||||
private long? _activeCategoryId;
|
private long? _activeCategoryId;
|
||||||
private string? _activeCategoryTitle;
|
private string? _activeCategoryTitle;
|
||||||
@@ -36,7 +41,7 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
await Cart.EnsureInitializedAsync();
|
await Cart.EnsureInitializedAsync();
|
||||||
Cart.OnChange += StateHasChanged;
|
Cart.OnChange += StateHasChanged;
|
||||||
Navigation.LocationChanged += HandleLocationChanged;
|
Navigation.LocationChanged += HandleLocationChanged;
|
||||||
await Load();
|
await LoadInitial();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
@@ -49,18 +54,40 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Load()
|
private async Task LoadInitial()
|
||||||
{
|
{
|
||||||
_loading = true;
|
_loading = true;
|
||||||
|
_currentPage = 1;
|
||||||
|
_products.Clear();
|
||||||
UpdateCategoryFilterFromUri();
|
UpdateCategoryFilterFromUri();
|
||||||
var sortBy = GetSortByValue();
|
var sortBy = GetSortByValue();
|
||||||
_products = await ProductService.GetProductsAsync(_query, _activeCategoryId, sortBy);
|
var result = await ProductService.GetProductsPagedAsync(_query, _activeCategoryId, sortBy, _currentPage, PageSize);
|
||||||
|
_products = result.Products;
|
||||||
|
_hasMore = result.HasNext;
|
||||||
|
_totalCount = result.TotalCount;
|
||||||
_activeCategoryTitle = _activeCategoryId is { } categoryId
|
_activeCategoryTitle = _activeCategoryId is { } categoryId
|
||||||
? (await CategoryService.GetByIdAsync(categoryId))?.Title
|
? (await CategoryService.GetByIdAsync(categoryId))?.Title
|
||||||
: null;
|
: null;
|
||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadMore()
|
||||||
|
{
|
||||||
|
if (_loadingMore || !_hasMore) return;
|
||||||
|
|
||||||
|
_loadingMore = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
_currentPage++;
|
||||||
|
var sortBy = GetSortByValue();
|
||||||
|
var result = await ProductService.GetProductsPagedAsync(_query, _activeCategoryId, sortBy, _currentPage, PageSize);
|
||||||
|
_products.AddRange(result.Products);
|
||||||
|
_hasMore = result.HasNext;
|
||||||
|
|
||||||
|
_loadingMore = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private string GetSortByValue()
|
private string GetSortByValue()
|
||||||
{
|
{
|
||||||
return _sortOption switch
|
return _sortOption switch
|
||||||
@@ -75,12 +102,12 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
|
|
||||||
private async Task OnSortChanged()
|
private async Task OnSortChanged()
|
||||||
{
|
{
|
||||||
await Load();
|
await LoadInitial();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnQueryChanged(KeyboardEventArgs _)
|
private async Task OnQueryChanged(KeyboardEventArgs _)
|
||||||
{
|
{
|
||||||
await Load();
|
await LoadInitial();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddToCart(Product p)
|
private async Task AddToCart(Product p)
|
||||||
@@ -98,7 +125,7 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
|
|
||||||
private void HandleLocationChanged(object? sender, LocationChangedEventArgs args)
|
private void HandleLocationChanged(object? sender, LocationChangedEventArgs args)
|
||||||
{
|
{
|
||||||
_ = InvokeAsync(Load);
|
_ = InvokeAsync(LoadInitial);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateCategoryFilterFromUri()
|
private void UpdateCategoryFilterFromUri()
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ public record Product(
|
|||||||
= [];
|
= [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record ProductListResult(
|
||||||
|
List<Product> Products,
|
||||||
|
int TotalCount,
|
||||||
|
int TotalPages,
|
||||||
|
int CurrentPage,
|
||||||
|
bool HasNext);
|
||||||
|
|
||||||
public record ProductGalleryImage(
|
public record ProductGalleryImage(
|
||||||
long ProductGalleryId,
|
long ProductGalleryId,
|
||||||
long ProductImageId,
|
long ProductImageId,
|
||||||
@@ -59,11 +66,24 @@ public class ProductService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Product>> GetProductsAsync(string? query = null, long? categoryId = null, string? sortBy = null)
|
public async Task<List<Product>> GetProductsAsync(string? query = null, long? categoryId = null, string? sortBy = null)
|
||||||
|
{
|
||||||
|
var result = await GetProductsPagedAsync(query: query, categoryId: categoryId, sortBy: sortBy, page: 1, pageSize: 500);
|
||||||
|
return result.Products;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ProductListResult> GetProductsPagedAsync(
|
||||||
|
string? query = null, long? categoryId = null, string? sortBy = null,
|
||||||
|
int page = 1, int pageSize = 12)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var request = new GetAllProductsByFilterRequest
|
var request = new GetAllProductsByFilterRequest
|
||||||
{
|
{
|
||||||
|
PaginationState = new CMSMicroservice.Protobuf.Protos.PaginationState
|
||||||
|
{
|
||||||
|
PageNumber = page,
|
||||||
|
PageSize = pageSize
|
||||||
|
},
|
||||||
Filter = new GetAllProductsByFilterFilter
|
Filter = new GetAllProductsByFilterFilter
|
||||||
{
|
{
|
||||||
Title = query ?? string.Empty,
|
Title = query ?? string.Empty,
|
||||||
@@ -84,7 +104,12 @@ public class ProductService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp = await _client.GetAllProductsByFilterAsync(request);
|
var resp = await _client.GetAllProductsByFilterAsync(request);
|
||||||
return MapAndCache(resp.Models);
|
var products = MapAndCache(resp.Models);
|
||||||
|
var totalCount = (int)(resp.MetaData?.TotalCount ?? 0);
|
||||||
|
var totalPages = (int)(resp.MetaData?.TotalPage ?? 0);
|
||||||
|
var hasNext = resp.MetaData?.HasNext ?? false;
|
||||||
|
|
||||||
|
return new ProductListResult(products, totalCount, totalPages, page, hasNext);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -97,7 +122,8 @@ public class ProductService
|
|||||||
p.Description.Contains(q, StringComparison.OrdinalIgnoreCase));
|
p.Description.Contains(q, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.OrderBy(p => p.Id).ToList();
|
var all = list.OrderBy(p => p.Id).ToList();
|
||||||
|
return new ProductListResult(all, all.Count, 1, 1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user