forked from tsai/eShopOnAbp
committed by
GitHub
37 changed files with 478 additions and 152 deletions
@ -0,0 +1,76 @@ |
|||
using Castle.Core.Logging; |
|||
using EShopOnAbp.BasketService; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace EShopOnAbp.PublicWeb.Basket |
|||
{ |
|||
public class UserBasketProvider : ITransientDependency |
|||
{ |
|||
protected HttpContext HttpContext => httpContextAccessor.HttpContext; |
|||
|
|||
private readonly IHttpContextAccessor httpContextAccessor; |
|||
protected readonly ILogger<UserBasketProvider> logger; |
|||
protected readonly IGuidGenerator guidGenerator; |
|||
protected readonly IBasketAppService basketAppService; |
|||
protected readonly ICurrentUser currentUser; |
|||
|
|||
public UserBasketProvider( |
|||
IHttpContextAccessor httpContextAccessor, |
|||
ILogger<UserBasketProvider> logger, |
|||
IGuidGenerator guidGenerator, |
|||
IBasketAppService basketAppService, |
|||
ICurrentUser currentUser) |
|||
{ |
|||
this.httpContextAccessor = httpContextAccessor; |
|||
this.logger = logger; |
|||
this.guidGenerator = guidGenerator; |
|||
this.basketAppService = basketAppService; |
|||
this.currentUser = currentUser; |
|||
} |
|||
|
|||
public virtual async Task<BasketDto> GetBasketAsync() |
|||
{ |
|||
try |
|||
{ |
|||
// Get anonymous user id from cookie
|
|||
HttpContext.Request.Cookies.TryGetValue(CookieConstants.AnonymousUserCookieName, |
|||
out string anonymousUserId); |
|||
logger.LogInformation($"========= Anonymous User Id from Cookie:{anonymousUserId} ========= "); |
|||
|
|||
// Generate new id for anonymous user
|
|||
if (string.IsNullOrEmpty(anonymousUserId)) |
|||
{ |
|||
anonymousUserId = guidGenerator.Create().ToString(); |
|||
HttpContext.Response.Cookies.Append(CookieConstants.AnonymousUserCookieName, anonymousUserId); |
|||
logger.LogInformation($"========= Generated new User Id:{anonymousUserId} ========= APPENDED TO COOKIE ====== "); |
|||
} |
|||
|
|||
if (!currentUser.IsAuthenticated) |
|||
{ |
|||
logger.LogInformation( |
|||
$"========= Get Basket for Anonymous UserId:{anonymousUserId} ========= APPENDED TO COOKIE ====== "); |
|||
return await basketAppService.GetByAnonymousUserIdAsync(Guid.Parse(anonymousUserId)); |
|||
} |
|||
else |
|||
{ |
|||
//TODO: Merge with anonymously stored cart if exist
|
|||
return await basketAppService.GetAsync(); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
logger.LogError(ex, ex.Message); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,29 +1,31 @@ |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.PublicWeb.Basket; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
using EShopOnAbp.BasketService; |
|||
|
|||
namespace EShopOnAbp.PublicWeb.Components.Basket; |
|||
|
|||
[Widget( |
|||
AutoInitialize = true, |
|||
RefreshUrl = "/Widgets/Basket", |
|||
StyleFiles = new[] { "/components/basket-widget.css" }, |
|||
ScriptTypes = new[] { typeof(BasketWidgetScriptContributor) } |
|||
StyleFiles = new[] {"/components/basket-widget.css"}, |
|||
ScriptTypes = new[] {typeof(BasketWidgetScriptContributor)} |
|||
)] |
|||
public class BasketWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
private readonly IBasketAppService _basketAppService; |
|||
private readonly UserBasketProvider userBasketProvider; |
|||
|
|||
public BasketWidgetViewComponent(IBasketAppService basketAppService) |
|||
public BasketWidgetViewComponent(UserBasketProvider userBasketProvider) |
|||
{ |
|||
_basketAppService = basketAppService; |
|||
this.userBasketProvider = userBasketProvider; |
|||
} |
|||
|
|||
public async Task<IViewComponentResult> InvokeAsync() |
|||
{ |
|||
var basketDto = await _basketAppService.GetAsync(); |
|||
return View("~/Components/Basket/Default.cshtml", basketDto); |
|||
|
|||
return View( |
|||
"~/Components/Basket/Default.cshtml", |
|||
await userBasketProvider.GetBasketAsync()); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EShopOnAbp.PublicWeb.Components.Toolbar.Cart; |
|||
|
|||
[DependsOn(typeof(SignalRBrowserScriptContributor))] |
|||
public class CartWidgetScriptContributor: BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace EShopOnAbp.PublicWeb.Components.Toolbar.Cart; |
|||
|
|||
public class CartWidgetStyleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/components/cart/cart-widget.css"); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using EShopOnAbp.PublicWeb.Basket; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace EShopOnAbp.PublicWeb.Components.Toolbar.Cart; |
|||
|
|||
[Widget( |
|||
AutoInitialize = true, |
|||
RefreshUrl = "/Widgets/Cart", |
|||
StyleTypes = new[] {typeof(CartWidgetStyleContributor)}, |
|||
ScriptTypes = new[] {typeof(CartWidgetScriptContributor)} |
|||
)] |
|||
public class CartWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
private readonly UserBasketProvider userBasketProvider; |
|||
|
|||
public CartWidgetViewComponent(UserBasketProvider userBasketProvider) |
|||
{ |
|||
this.userBasketProvider = userBasketProvider; |
|||
} |
|||
|
|||
public async Task<IViewComponentResult> InvokeAsync() |
|||
{ |
|||
return View( |
|||
"~/Components/Toolbar/Cart/Default.cshtml", |
|||
await userBasketProvider.GetBasketAsync()); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
@using EShopOnAbp.BasketService.Localization |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@inject IHtmlLocalizer<BasketServiceResource> L |
|||
@model EShopOnAbp.BasketService.BasketDto |
|||
<div id="shopping-cart" class="nav-link"> |
|||
@if (Model == null) |
|||
{ |
|||
<span class="cart-error" style="vertical-align: -webkit-baseline-middle;color: white">@L["Toolbar:BasketNotLoaded"] </span> |
|||
} |
|||
else |
|||
{ |
|||
<a class="btn nav-link" style="padding:0px" role="button" href="~/Basket"> |
|||
<span abp-badge="Info" data-cart-count>@Model.Items.Count</span> @L["Toolbar:Cart"] |
|||
</a> |
|||
} |
|||
</div> |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace EShopOnAbp.PublicWeb; |
|||
|
|||
public static class CookieConstants |
|||
{ |
|||
public const string AnonymousUserCookieName = "eshop_anonymousId"; |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
@page |
|||
@using EShopOnAbp.Localization |
|||
@using EShopOnAbp.PublicWeb.Components.Basket |
|||
@using Microsoft.Extensions.Localization |
|||
@model EShopOnAbp.PublicWeb.Pages.Basket |
|||
|
|||
@section scripts { |
|||
<abp-script src="/client-proxies/basket-proxy.js" /> |
|||
<abp-script src="/Pages/index.js" /> |
|||
} |
|||
|
|||
@inject IStringLocalizer<EShopOnAbpResource> L |
|||
|
|||
<div class="col-lg-12"> |
|||
<h3 class="pt-5 pb-4 text-center">@L["YourBasket"]</h3> |
|||
<div id="BasketArea"> |
|||
@await Component.InvokeAsync(typeof(BasketWidgetViewComponent)) |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,11 @@ |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
|
|||
namespace EShopOnAbp.PublicWeb.Pages; |
|||
|
|||
public class Basket : PageModel |
|||
{ |
|||
public void OnGet() |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
(function(){ |
|||
$(function(){ |
|||
|
|||
var basketWidget = new abp.WidgetManager('#BasketArea'); |
|||
|
|||
$('.product-list-item').click(function(){ |
|||
var $this = $(this); |
|||
var productId = $this.attr('data-product-id'); |
|||
eShopOnAbp.basketService.basket.addProduct({ |
|||
productId: productId |
|||
}).then(function(){ |
|||
basketWidget.refresh(); |
|||
abp.notify.success("Added product to your basket.", "Successfully added"); |
|||
}); |
|||
}); |
|||
}); |
|||
})(); |
|||
@ -1,72 +1,60 @@ |
|||
@page |
|||
@using EShopOnAbp.Localization |
|||
@using Microsoft.Extensions.Localization |
|||
@model EShopOnAbp.PublicWeb.Pages.IndexModel |
|||
@inject IStringLocalizer<EShopOnAbpResource> L |
|||
|
|||
<abp-card Class=" overflow-hidden"> |
|||
<abp-card-body Class="p-0"> |
|||
<abp-carousel style="width: 100%;" indicators="true" controls="true"> |
|||
<abp-carousel-item src="https://i.picsum.photos/id/1002/1200/600.jpg?hmac=vEtEOHyKsKb7OglkR2kK2kT3SNT5KHJ4wtetm49EDIY" style="height: 600px;" class="bg-primary" caption-title="Maecenas a enim purus" caption="Sed sed tristique diam. Sed malesuada eu ex eu facilisis. Donec commodo placerat enim, vel mollis sem consequat eget. Pellentesque eget cursus sapien."></abp-carousel-item> |
|||
<abp-carousel-item src="https://i.picsum.photos/id/1070/1200/600.jpg?hmac=MvQe1lH2C5sz8TXGRo0pMg2Pcbvri9rpG_rvoBFpqxw" style="height: 600px;" class="bg-primary" caption-title="Sed sed tristique diam. " caption="Sed malesuada eu ex eu facilisis. Donec commodo placerat enim, vel mollis sem consequat eget. Pellentesque eget cursus sapien."></abp-carousel-item> |
|||
<abp-carousel-item src="https://i.picsum.photos/id/841/1200/600.jpg?hmac=1dnXZo7goR6mFJqBs-5o3jUdMke1v-JCGCl0on-cng8" style="height: 600px;" class="bg-primary" caption-title="Donec commodo placerat" caption="Sed sed tristique diam. Sed malesuada eu ex eu facilisis. Vel mollis sem consequat eget. Pellentesque eget cursus sapien."></abp-carousel-item> |
|||
</abp-carousel> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
@{ |
|||
const int productsColumnSize = 4; // TODO: update abp-column helper to plain html for proper row and columns css |
|||
} |
|||
|
|||
<div class="container"> |
|||
<div class="row text-center py-5"> |
|||
<div class="col-lg-4 py-4"> |
|||
<i class="fas fa-chess-rook fa-4x text-primary d-block my-3"></i> |
|||
<h2>Heading</h2> |
|||
<p class="lead">Vestibulum id ligula porta.</p> |
|||
<p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p> |
|||
<p><a class="btn btn-outline-primary" href="#" role="button">View details »</a></p> |
|||
</div><!-- /.col-lg-4 --> |
|||
<div class="col-lg-4 py-4"> |
|||
<i class="fas fa-bullhorn fa-4x text-info d-block my-3"></i> |
|||
<h2>Heading</h2> |
|||
<p class="lead">Nulla non metus auctor.</p> |
|||
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p> |
|||
<p><a class="btn btn-outline-info" href="#" role="button">View details »</a></p> |
|||
</div><!-- /.col-lg-4 --> |
|||
<div class="col-lg-4 py-4"> |
|||
<i class="fas fa-balance-scale fa-4x text-success d-block my-3"></i> |
|||
<h2>Heading</h2> |
|||
<p class="lead">Metus auctor fringilla.</p> |
|||
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, sit amet risus.</p> |
|||
<p><a class="btn btn-outline-success" href="#" role="button">View details »</a></p> |
|||
</div><!-- /.col-lg-4 --> |
|||
</div><!-- /.row --> |
|||
@section styles{ |
|||
<abp-style src="/Pages/index.css"/> |
|||
} |
|||
@section scripts { |
|||
<abp-script src="/client-proxies/basket-proxy.js" /> |
|||
<abp-script src="/Pages/index.js" /> |
|||
} |
|||
|
|||
<div class="row my-5 py-md-5"> |
|||
<div class="col-md-6 pr-lg-5"> |
|||
<h2 class="mt-5">First featurette heading. <span class="text-primary d-block">It'll blow your mind.</span></h2> |
|||
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. </p> |
|||
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<img src="https://i.picsum.photos/id/59/600/400.jpg?hmac=hCS_vJsQfuK1hUlwEUlGbUAnb_pFljGkD-2jdYxB1y8" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row my-5 py-md-5"> |
|||
<div class="col-md-6 order-md-2 pl-lg-5"> |
|||
<h2 class="mt-5">Oh yeah, it's that good. <span class="text-primary d-block">See for yourself.</span></h2> |
|||
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. </p> |
|||
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p> |
|||
</div> |
|||
<div class="col-md-6 order-md-1"> |
|||
<img src="https://i.picsum.photos/id/437/600/400.jpg?hmac=JNv4AQJrQn53c1Xh1C7q0NBNoJgRXpdKtzIDJ1w2lqA" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<h3 class="pt-5 pb-4 text-center">@L["OurProducts"]</h3> |
|||
<div class="product-list"> |
|||
@for (int i = 0; i < Math.Ceiling(Model.Products.Count / (double)productsColumnSize); i++) |
|||
{ |
|||
<abp-row> |
|||
@for (int j = 0; j < productsColumnSize; j++) |
|||
{ |
|||
<abp-column size-lg="_3" size-md="_4" size-sm="_6"> |
|||
@if ((i * productsColumnSize) + j < Model.Products.Count) |
|||
{ |
|||
var product = Model.Products[(i * productsColumnSize) + j]; |
|||
<div class="product-list-item" data-product-id="@product.Id.ToString()"> |
|||
@if (!product.ImageName.IsNullOrEmpty()) |
|||
{ |
|||
<img src="/product-images/@product.ImageName"/> |
|||
} |
|||
else |
|||
{ |
|||
<img src="/product-images/noimage.jpg"/> |
|||
} |
|||
<div class="row mt-5"> |
|||
<div class="col"> |
|||
<div class="product-info-box"> |
|||
<h6>@product.Name</h6> |
|||
<em class="d-block text-muted">@product.Code - @L["StockCount"]: @product.StockCount</em> |
|||
</div> |
|||
</div> |
|||
<div class="col-auto text-right text-danger"> |
|||
<strong class="product-price">$@product.Price</strong> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
} |
|||
</abp-column> |
|||
} |
|||
</abp-row> |
|||
} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row my-5 py-md-5"> |
|||
<div class="col-md-6 pr-lg-5"> |
|||
<h2 class="mt-5">And lastly, this one. <span class="text-primary d-block">Checkmate.</span></h2> |
|||
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. </p> |
|||
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<img src="https://i.picsum.photos/id/835/600/400.jpg?hmac=HR6Bi8aji8auDURA6j9xiZC6QgcFwwBTMJ8X4Cf6bPg" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,26 @@ |
|||
(function () { |
|||
//set anonymous user
|
|||
let anonymousId = abp.utils.getCookieValue("eshop_anonymousId"); |
|||
if (anonymousId != null) { |
|||
console.info("Anonymous user set:" + anonymousId); |
|||
} |
|||
|
|||
$(function () { |
|||
|
|||
$('.product-list-item').click(function () { |
|||
var $this = $(this); |
|||
var productId = $this.attr('data-product-id'); |
|||
eShopOnAbp.basketService.basket.addProduct({ |
|||
productId: productId, |
|||
anonymousId: anonymousId, |
|||
}).then(function () { |
|||
|
|||
$('.abp-widget-wrapper[data-widget-name="CartWidget"]') |
|||
.data('abp-widget-manager') |
|||
.refresh(); |
|||
|
|||
abp.notify.success("Added product to your basket.", "Successfully added"); |
|||
}); |
|||
}); |
|||
}); |
|||
})(); |
|||
@ -0,0 +1,4 @@ |
|||
.cart-error { |
|||
vertical-align: -webkit-baseline-middle; |
|||
color: rgba(255, 255, 255, 0.75); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
|
|||
namespace EShopOnAbp.BasketService |
|||
{ |
|||
public interface IHasAnonymousId |
|||
{ |
|||
Guid? AnonymousId { get; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue