Browse Source

Introduce a quick-start home page in the sample app

pull/172/head
gdlcf88 4 years ago
parent
commit
3dbf03a57a
  1. 31
      samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json
  2. 43
      samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json
  3. 43
      samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json
  4. 7
      samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataConsts.cs
  5. 181
      samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs
  6. 1
      samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj
  7. 115
      samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml
  8. 137
      samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs
  9. 53
      samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.css
  10. 128
      samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.js
  11. 299
      samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/SkuSelector.js

31
samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/en.json

@ -4,6 +4,35 @@
"Menu:EasyAbpEShop": "EShop",
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
"LongWelcomeMessage": "Welcome to the application. This is a demo project of the EShop module. For more information, visit easyabp.io.",
"Product": "Product",
"Cake": "Cake",
"OrderCake": "Order a cake",
"CreateOrder": "Create order",
"PayForOrder": "Pay for order",
"TopUp-1-CNY": "Top-up 1.00 CNY",
"OrderHistory": "Order history",
"Flavor": "Flavor",
"Size": "Size",
"Chocolate": "Chocolate",
"Vanilla": "Vanilla",
"YourChoice": "Your choice",
"SkuId": "SKU ID",
"OrderCreated": "Order created",
"OrderCanceled": "Order canceled",
"TopUpSucceeded": "Top-up succeeded",
"PaymentSucceeded": "Payment succeeded",
"InsufficientBalance": "Insufficient account balance",
"OrderNumber": "Order number",
"Created": "Created",
"OrderStatus": "Status",
"OrderStatusPending": "Pending",
"OrderStatusProcessing": "Processing",
"OrderStatusCompleted": "Completed",
"OrderStatusCanceled": "Canceled",
"TotalPrice": "Price",
"AccountBalance": "Account balance",
"TimeToAutoCancel": "Order auto-cancel in",
"CancelOrder": "Cancel order"
}
}

43
samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hans.json

@ -1,9 +1,38 @@
{
"culture": "zh-Hans",
"texts": {
"culture": "zh-Hans",
"texts": {
"Menu:EasyAbpEShop": "EShop 商城",
"Menu:Home": "首页",
"Welcome": "欢迎",
"LongWelcomeMessage": "欢迎来到该应用程序. 这是一个基于ABP框架的启动项目. 有关更多信息, 请访问 abp.io."
}
}
"Menu:Home": "首页",
"Welcome": "欢迎",
"LongWelcomeMessage": "欢迎来到该应用程序. 这是一个 EShop 模块的示例项目. 有关更多信息, 请访问 easyabp.io.",
"Product": "商品",
"Cake": "蛋糕",
"OrderCake": "下单买蛋糕",
"CreateOrder": "创建订单",
"PayForOrder": "支付订单",
"TopUp-1-CNY": "充值 1.00 元",
"OrderHistory": "历史订单",
"Flavor": "口味",
"Size": "分量",
"Chocolate": "巧克力味",
"Vanilla": "香草味",
"YourChoice": "你的选择",
"SkuId": "SKU ID",
"OrderCreated": "订单创建成功",
"OrderCanceled": "订单已取消",
"TopUpSucceeded": "充值成功",
"PaymentSucceeded": "支付成功",
"InsufficientBalance": "账户余额不足",
"OrderNumber": "订单编号",
"Created": "创建时间",
"OrderStatus": "状态",
"OrderStatusPending": "待支付",
"OrderStatusProcessing": "进行中",
"OrderStatusCompleted": "已完成",
"OrderStatusCanceled": "已取消",
"TotalPrice": "价格",
"AccountBalance": "账户余额",
"TimeToAutoCancel": "剩余可支付时间",
"CancelOrder": "取消订单"
}
}

43
samples/EShopSample/aspnet-core/src/EShopSample.Domain.Shared/Localization/EShopSample/zh-Hant.json

@ -1,9 +1,38 @@
{
"culture": "zh-Hant",
"texts": {
"culture": "zh-Hant",
"texts": {
"Menu:EasyAbpEShop": "EShop 商城",
"Menu:Home": "首頁",
"Welcome": "歡迎",
"LongWelcomeMessage": "歡迎來到此應用程式. 這是一個基於ABP框架的起始專案. 有關更多訊息, 請瀏覽 abp.io."
}
}
"Menu:Home": "首頁",
"Welcome": "歡迎",
"LongWelcomeMessage": "歡迎來到此應用程式. 這是一個 EShop 模組的示例專案. 有關更多訊息, 請瀏覽 easyabp.io.",
"Product": "商品",
"Cake": "蛋糕",
"OrderCake": "下單買蛋糕",
"CreateOrder": "創建訂單",
"PayForOrder": "支付訂單",
"TopUp-1-CNY": "給賬戶充值",
"OrderHistory": "歷史訂單",
"Flavor": "口味",
"Size": "分量",
"Chocolate": "巧克力味",
"Vanilla": "香草味",
"YourChoice": "你的選擇",
"SkuId": "SKU ID",
"OrderCreated": "訂單創建成功",
"OrderCanceled": "訂單已取消",
"TopUpSucceeded": "充值成功",
"PaymentSucceeded": "支付成功",
"InsufficientBalance": "賬戶餘額不足",
"OrderNumber": "訂單編號",
"Created": "創建時間",
"OrderStatus": "狀態",
"OrderStatusPending": "待支付",
"OrderStatusProcessing": "進行中",
"OrderStatusCompleted": "已完成",
"OrderStatusCanceled": "已取消",
"TotalPrice": "價格",
"AccountBalance": "賬戶餘額",
"TimeToAutoCancel": "剩餘可支付時間",
"CancelOrder": "取消訂單"
}
}

7
samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataConsts.cs

@ -0,0 +1,7 @@
namespace EShopSample.Data;
public static class SampleDataConsts
{
public const string FoodsCategoryUniqueName = "Foods";
public const string CakeProductUniqueName = "Cake";
}

181
samples/EShopSample/aspnet-core/src/EShopSample.Domain/Data/SampleDataSeedContributor.cs

@ -0,0 +1,181 @@
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Products;
using EasyAbp.EShop.Products.Categories;
using EasyAbp.EShop.Products.ProductCategories;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Stores.Settings;
using EasyAbp.EShop.Stores.Stores;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
namespace EShopSample.Data;
public class SampleDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
private readonly IStoreRepository _storeRepository;
private readonly IProductManager _productManager;
private readonly IProductRepository _productRepository;
private readonly ICategoryManager _categoryManager;
private readonly ICategoryRepository _categoryRepository;
private readonly IProductCategoryRepository _productCategoryRepository;
private readonly ISettingProvider _settingProvider;
private readonly IAttributeOptionIdsSerializer _attributeOptionIdsSerializer;
public SampleDataSeedContributor(
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant,
IStoreRepository storeRepository,
IProductManager productManager,
IProductRepository productRepository,
ICategoryManager categoryManager,
ICategoryRepository categoryRepository,
IProductCategoryRepository productCategoryRepository,
ISettingProvider settingProvider,
IAttributeOptionIdsSerializer attributeOptionIdsSerializer)
{
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
_storeRepository = storeRepository;
_productManager = productManager;
_productRepository = productRepository;
_categoryManager = categoryManager;
_categoryRepository = categoryRepository;
_productCategoryRepository = productCategoryRepository;
_settingProvider = settingProvider;
_attributeOptionIdsSerializer = attributeOptionIdsSerializer;
}
[UnitOfWork(true)]
public virtual async Task SeedAsync(DataSeedContext context)
{
using var changeTenant = _currentTenant.Change(context.TenantId);
await SeedStoresAsync();
await SeedCategoriesAsync();
await SeedProductsAsync();
await SeedProductCategoryMappingsAsync();
}
protected virtual async Task SeedProductCategoryMappingsAsync()
{
var product = await _productRepository.GetAsync(
x => x.UniqueName == SampleDataConsts.CakeProductUniqueName);
var category = await _categoryRepository.GetAsync(
x => x.UniqueName == SampleDataConsts.FoodsCategoryUniqueName);
if (await _productCategoryRepository.AnyAsync(x => x.ProductId == product.Id && x.CategoryId == category.Id))
{
return;
}
await _productCategoryRepository.InsertAsync(
new ProductCategory(_guidGenerator.Create(), _currentTenant.Id, category.Id, product.Id));
}
public virtual async Task SeedStoresAsync()
{
var storeName = await _settingProvider.GetOrNullAsync(StoresSettings.DefaultStoreName);
if (await _storeRepository.AnyAsync(x => x.Name == storeName))
{
return;
}
await _storeRepository.InsertAsync(new Store(_guidGenerator.Create(), _currentTenant.Id, storeName), true);
}
public virtual async Task SeedCategoriesAsync()
{
if (await _categoryRepository.AnyAsync(x => x.UniqueName == SampleDataConsts.FoodsCategoryUniqueName))
{
return;
}
var category = await _categoryManager.CreateAsync(null, SampleDataConsts.FoodsCategoryUniqueName, "Foods",
"Some delicious foods.", null, false);
await _categoryRepository.InsertAsync(category, true);
}
public virtual async Task SeedProductsAsync()
{
if (await _productRepository.AnyAsync(x => x.UniqueName == SampleDataConsts.CakeProductUniqueName))
{
return;
}
var defaultStore = await _storeRepository.FindDefaultStoreAsync();
var product = new Product(
_guidGenerator.Create(),
_currentTenant.Id,
defaultStore.Id,
ProductsConsts.DefaultProductGroupName,
null,
SampleDataConsts.CakeProductUniqueName,
"Cake",
InventoryStrategy.NoNeed,
null,
true,
false,
false,
TimeSpan.FromMinutes(15),
null,
0);
var attribute1 = new ProductAttribute(_guidGenerator.Create(), "Size", null, 2);
var attribute2 = new ProductAttribute(_guidGenerator.Create(), "Flavor", null, 1);
attribute1.ProductAttributeOptions.AddRange(new[]
{
new ProductAttributeOption(_guidGenerator.Create(), "S", null, 1),
new ProductAttributeOption(_guidGenerator.Create(), "M", null, 2),
new ProductAttributeOption(_guidGenerator.Create(), "L", null, 3),
});
attribute2.ProductAttributeOptions.AddRange(new[]
{
new ProductAttributeOption(_guidGenerator.Create(), "Chocolate", null, 1),
new ProductAttributeOption(_guidGenerator.Create(), "Vanilla", null, 2),
});
product.ProductAttributes.Add(attribute1);
product.ProductAttributes.Add(attribute2);
await _productManager.CreateAsync(product);
var productSku1 = new ProductSku(_guidGenerator.Create(),
await _attributeOptionIdsSerializer.SerializeAsync(new[]
{ attribute1.ProductAttributeOptions[0].Id, attribute2.ProductAttributeOptions[0].Id }),
null, "CNY", null, 1m, 1, 10, null, null, null);
var productSku2 = new ProductSku(_guidGenerator.Create(),
await _attributeOptionIdsSerializer.SerializeAsync(new[]
{ attribute1.ProductAttributeOptions[1].Id, attribute2.ProductAttributeOptions[0].Id }),
null, "CNY", null, 2m, 1, 10, null, null, null);
var productSku3 = new ProductSku(_guidGenerator.Create(),
await _attributeOptionIdsSerializer.SerializeAsync(new[]
{ attribute1.ProductAttributeOptions[1].Id, attribute2.ProductAttributeOptions[1].Id }),
null, "CNY", null, 3m, 1, 10, null, null, null);
var productSku4 = new ProductSku(_guidGenerator.Create(),
await _attributeOptionIdsSerializer.SerializeAsync(new[]
{ attribute1.ProductAttributeOptions[2].Id, attribute2.ProductAttributeOptions[1].Id }),
null, "CNY", null, 4m, 1, 10, null, null, null);
await _productManager.CreateSkuAsync(product, productSku1);
await _productManager.CreateSkuAsync(product, productSku2);
await _productManager.CreateSkuAsync(product, productSku3);
await _productManager.CreateSkuAsync(product, productSku4);
}
}

1
samples/EShopSample/aspnet-core/src/EShopSample.Web/EShopSample.Web.csproj

@ -20,6 +20,7 @@
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<EmbeddedResource Include="Pages\SkuSelector.js" />
<None Remove="Logs\**" />
</ItemGroup>

115
samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml

@ -1,16 +1,33 @@
@page
@inherits EShopSample.Web.Pages.EShopSamplePage
@using EasyAbp.EShop.Orders.Orders
@model EShopSample.Web.Pages.IndexModel
@section styles {
<abp-style-bundle>
<abp-style src="/Pages/Index.css" />
<abp-style src="/Pages/Index.css"/>
</abp-style-bundle>
}
@section scripts {
<abp-script-bundle>
<abp-script src="/Pages/Index.js" />
<abp-script src="/Pages/Index.js"/>
<abp-script src="/Pages/SkuSelector.js"/>
</abp-script-bundle>
}
<script>
let storeId = '@Model.Store?.Id'
let orderId = '@Model.Order?.Id'
let cakeProductId = '@Model.CakeProduct?.Id'
let cakeProductSkuId = null
let accountId = '@Model.Wallet?.Id'
let currentBalance = @(Model.Wallet?.Balance ?? 0m)
let totalPrice = @(Model.Order?.ActualTotalPrice ?? 0m)
let skuInfo = '@(Model.CakeProduct is not null ? Html.Raw(Model.GetJsonSkuInfo()) : "[]")'
let secondsToAutoCancel = @Model.GetSecondsToAutoCancel()
</script>
<div class="jumbotron text-center">
<h1>@L["Welcome"]</h1>
<div class="row">
@ -19,9 +36,99 @@
<hr class="my-4"/>
</div>
</div>
<a href="https://abp.io?ref=tmpl" target="_blank" class="btn btn-primary px-4">abp.io</a>
@if (!CurrentUser.IsAuthenticated)
{
<a abp-button="Primary" href="/Account/Login" class="px-4"><i class="fa fa-sign-in"></i> @L["Login"]</a>
<a abp-button="Primary" href="/Account/Login" class="px-4">
<i class="fa fa-sign-in"></i> @L["Login"]
</a>
}
else
{
<div class="row">
<div class="col-md-6 mx-auto">
<abp-tabs>
<abp-tab name="OrderCakeTab" title="@L["OrderCake"].Value" active="@(Model.Order is null)">
<h1>@L[Model.CakeProduct.DisplayName]</h1>
<hr>
<div id="skuSelector"></div>
<hr>
<div id="selectedSku" class="my-1"></div>
<div id="selectedSkuId" class="my-1"></div>
<div id="selectedSkuPrice" class="my-1"></div>
<hr>
<abp-button id="CreateOrderButton" button-type="Primary">@L["CreateOrder"]</abp-button>
</abp-tab>
<abp-tab name="PayForOrderTab" title="@L["PayForOrder"].Value" active="@(Model.Order?.OrderStatus is OrderStatus.Pending)">
@if (Model.Order is not null)
{
<abp-table hoverable-rows="true" responsive-sm="true">
<tr>
<td>@L["OrderNumber"]</td>
<td>@Model.Order.OrderNumber</td>
</tr>
<tr>
<td>@L["Product"]</td>
<td>@L[Model.CakeProduct.DisplayName]</td>
</tr>
@foreach (var optionId in Model.CakeProduct.ProductSkus.First(x => x.Id == Model.Order.OrderLines[0].ProductSkuId).AttributeOptionIds)
{
var option = Model.CakeProduct.ProductAttributes.SelectMany(x => x.ProductAttributeOptions)
.Single(x => x.Id == optionId);
var attr = Model.CakeProduct.ProductAttributes.Single(x => x.ProductAttributeOptions.Contains(option));
<tr>
<td>@L[attr.DisplayName]</td>
<td>@L[option.DisplayName]</td>
</tr>
}
<tr>
<td>@L["TotalPrice"]</td>
<td>@Model.Order.ActualTotalPrice.ToString("F2") @Model.Order.Currency</td>
</tr>
<tr>
<td>@L["AccountBalance"]</td>
<td>@Model.Wallet.Balance.ToString("F2") @Model.Order.Currency</td>
</tr>
</abp-table>
<div class="row">
<div class="col-md-6 mx-auto">
<p>
@L["TimeToAutoCancel"] <span id="timeToAutoCancel">?</span>
</p>
</div>
</div>
<abp-button id="CancelOrderButton" button-type="Info">@L["CancelOrder"]</abp-button>
<abp-button id="TopUpButton" button-type="Secondary">@L["TopUp-1-CNY"]</abp-button>
<abp-button id="PayForOrderButton" button-type="Primary">@L["PayForOrder"]</abp-button>
}
</abp-tab>
<abp-tab name="OrderHistoryTab" title="@L["OrderHistory"].Value">
<abp-table hoverable-rows="true" responsive-sm="true">
<thead>
<tr>
<th scope="Column">#</th>
<th scope="Column">@L["OrderNumber"]</th>
<th scope="Column">@L["Created"]</th>
<th scope="Column">@L["OrderStatus"]</th>
</tr>
</thead>
<tbody>
@{
var index = Model.OrderList.TotalCount;
foreach (var order in Model.OrderList.Items)
{
<tr>
<th scope="Row">@(index--)</th>
<td>@order.OrderNumber</td>
<td>@order.CreationTime</td>
<td><span class="@Model.GetOrderStatusColumnClass(order)">@L[$"OrderStatus{order.OrderStatus}"]</span></td>
</tr>
}
}
</tbody>
</abp-table>
</abp-tab>
</abp-tabs>
</div>
</div>
}
</div>

137
samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.cshtml.cs

@ -1,10 +1,141 @@
namespace EShopSample.Web.Pages
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using EasyAbp.EShop.Stores.Stores;
using EasyAbp.EShop.Stores.Stores.Dtos;
using EasyAbp.PaymentService.Prepayment.Accounts;
using EasyAbp.PaymentService.Prepayment.Accounts.Dtos;
using EShopSample.Data;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Users;
namespace EShopSample.Web.Pages;
public class IndexModel : EShopSamplePageModel
{
public class IndexModel : EShopSamplePageModel
private readonly IOrderAppService _orderAppService;
private readonly IStoreAppService _storeAppService;
private readonly IProductAppService _productAppService;
private readonly IAccountAppService _accountAppService;
public OrderDto Order { get; set; }
public PagedResultDto<OrderDto> OrderList { get; set; }
public StoreDto Store { get; set; }
public ProductDto CakeProduct { get; set; }
public AccountDto Wallet { get; set; }
public IndexModel(
IOrderAppService orderAppService,
IStoreAppService storeAppService,
IProductAppService productAppService,
IAccountAppService accountAppService)
{
public void OnGet()
_orderAppService = orderAppService;
_storeAppService = storeAppService;
_productAppService = productAppService;
_accountAppService = accountAppService;
}
public async Task OnGetAsync()
{
if (CurrentUser.Id is null)
{
return;
}
OrderList = await _orderAppService.GetListAsync(new GetOrderListDto
{
MaxResultCount = 5,
CustomerUserId = CurrentUser.GetId(),
Sorting = "CreationTime DESC"
});
Order = OrderList.Items.FirstOrDefault(x => x.OrderStatus is OrderStatus.Pending);
Store = await _storeAppService.GetDefaultAsync();
CakeProduct = await _productAppService.GetByUniqueNameAsync(Store.Id, SampleDataConsts.CakeProductUniqueName);
Wallet = (await _accountAppService.GetListAsync(new GetAccountListInput { UserId = CurrentUser.Id })).Items[0];
}
public string GetJsonSkuInfo()
{
var sb = new StringBuilder("[");
foreach (var sku in CakeProduct.ProductSkus)
{
sb.Append('{');
sb.Append("\"skus\":");
sb.Append('{');
foreach (var optionId in sku.AttributeOptionIds)
{
var option = CakeProduct.ProductAttributes.SelectMany(x => x.ProductAttributeOptions)
.Single(x => x.Id == optionId);
var attribute = CakeProduct.ProductAttributes.Single(x => x.ProductAttributeOptions.Contains(option));
sb.Append($"\"{L[attribute.DisplayName].Value}\":\"{L[option.DisplayName].Value}\"");
if (optionId != sku.AttributeOptionIds.Last())
{
sb.Append(',');
}
}
sb.Append('}');
sb.Append($",\"skuId\":\"{sku.Id}\"");
sb.Append($",\"skuPrice\":{sku.Price}");
sb.Append($",\"skuCurrency\":\"{sku.Currency}\"");
sb.Append('}');
if (sku != CakeProduct.ProductSkus.Last())
{
sb.Append(',');
}
}
sb.Append(']');
return sb.ToString();
}
public int GetSecondsToAutoCancel()
{
if (Order is null)
{
return 0;
}
return Order.PaymentExpiration.HasValue
? Convert.ToInt32((Order.PaymentExpiration.Value - Clock.Now).TotalSeconds)
: 0;
}
public string GetOrderStatusColumnClass(OrderDto order)
{
if (order is null)
{
return string.Empty;
}
return order.OrderStatus switch
{
OrderStatus.Pending => "status-pending-text",
OrderStatus.Processing => "status-processing-text",
OrderStatus.Completed => "status-completed-text",
OrderStatus.Canceled => "status-canceled-text",
_ => throw new ArgumentOutOfRangeException()
};
}
}

53
samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.css

@ -1,3 +1,52 @@
body {
#skuSelector dt {
width: 100px;
text-align: right;
font-weight: normal;
padding-top: 6px;
}
#skuSelector dl {
clear: both;
overflow: hidden;
}
#skuSelector dl.hl {
background: #ddd;
}
#skuSelector dt, #skuSelector dd {
float: left;
line-height: 18px;
margin-left: 10px;
margin-bottom: 0;
}
#skuSelector button {
font-size: 14px;
font-weight: bold;
padding: 4px 4px;
}
#skuSelector .disabled {
color: #999;
border: 1px dashed #666;
}
#skuSelector .active {
color: red;
}
.status-pending-text {
color: orange;
}
.status-processing-text {
color: green;
}
.status-completed-text {
color: blue;
}
.status-canceled-text {
}

128
samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/Index.js

@ -1,3 +1,129 @@
$(function () {
abp.log.debug('Index.js initialized!');
const l = abp.localization.getResource('EasyAbpEShopEShopSample');
const orderService = easyAbp.eShop.orders.orders.order;
const eShopPaymentService = easyAbp.eShop.payments.payments.payment;
const paymentService = easyAbp.paymentService.payments.payment;
const accountService = easyAbp.paymentService.prepayment.accounts.account;
$('#CreateOrderButton').click(function (e) {
e.preventDefault();
if (!cakeProductSkuId) return
let btn = $(this)
btn.buttonBusy(true);
orderService.create({
storeId: storeId,
orderLines: [{
productId: cakeProductId,
productSkuId: cakeProductSkuId,
quantity: 1
}]
}).then(function () {
abp.message.success(l('OrderCreated'))
.then(function () {
window.location.reload();
})
}).catch(function () {
btn.buttonBusy(false);
});
});
let redirectToOrderHistory = function () {
document.location.href = document.location.origin + '?showOrderHistory=true'
}
$('#CancelOrderButton').click(function (e) {
e.preventDefault();
orderService.cancel(orderId, {})
.then(function () {
abp.message.success(l('OrderCanceled'))
.then(function () {
redirectToOrderHistory()
})
});
});
$('#TopUpButton').click(function (e) {
e.preventDefault();
accountService.changeBalance(accountId, {changedBalance: 1.00})
.then(function () {
abp.message.success(l('TopUpSucceeded'))
.then(function () {
window.location.reload();
})
});
});
$('#PayForOrderButton').click(function (e) {
e.preventDefault();
if (currentBalance < totalPrice) {
abp.message.error(l('InsufficientBalance'))
.then(function () {
window.location.reload();
})
return
}
eShopPaymentService.create({
paymentMethod: "Prepayment",
orderIds: [orderId]
}).then(function () {
getPaymentIdAndPay()
});
});
let getPaymentIdAndPay = function () {
orderService.get(orderId).then(function (order) {
if (!order) return
if (!order.paymentId) {
setTimeout(getPaymentIdAndPay, 500)
return
}
paymentService.pay(order.paymentId, {
extraProperties: {
"AccountId": accountId
}
}).then(function () {
abp.message.success(l('PaymentSucceeded'))
.then(function () {
redirectToOrderHistory()
})
})
})
}
let updateTimeToAutoCancel = function () {
$('#timeToAutoCancel').text(new Date(secondsToAutoCancel * 1000).toISOString().substring(11, 19))
}
let cancelCurrentPayment = function () {
if (!orderId) return
orderService.get(orderId).then(function (order) {
if (order.paymentId) {
paymentService.cancel(order.paymentId)
}
})
}
function tryGoToOrderHistoryTab() {
if (location.search.indexOf('showOrderHistory=true') < 0) return
$('#OrderHistoryTab-tab').tab("show")
history.pushState(null, null, location.origin)
// document.location.href = document.location.href.split('#')[0]
}
let init = function () {
tryGoToOrderHistoryTab()
updateTimeToAutoCancel()
cancelCurrentPayment()
}
init()
let interval = setInterval(function () {
if (secondsToAutoCancel <= 0) {
clearInterval(interval)
return
}
secondsToAutoCancel--
updateTimeToAutoCancel()
}, 1000);
});

299
samples/EShopSample/aspnet-core/src/EShopSample.Web/Pages/SkuSelector.js

@ -0,0 +1,299 @@
// Copied from https://codepen.io/keelii/pen/RoOzgb
const l = abp.localization.getResource('EasyAbpEShopEShopSample');
var data = JSON.parse(skuInfo)
var res = {}
var spliter = '\u2299'
var r = {}
var keys = []
var selectedCache = []
function combineAttr(data, keys) {
var allKeys = []
var result = {}
for (var i = 0; i < data.length; i++) {
var item = data[i].skus
var values = []
for (var j = 0; j < keys.length; j++) {
var key = keys[j]
if (!result[key]) result[key] = []
if (result[key].indexOf(item[key]) < 0) result[key].push(item[key])
values.push(item[key])
}
allKeys.push({
path: values.join(spliter),
sku: data[i].skuId,
price: data[i].skuPrice,
currency: data[i].skuCurrency
})
}
return {
result: result,
items: allKeys
}
}
function render(data) {
var output = ''
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var items = data[key]
output += '<dl data-type="' + key + '" data-idx="' + i + '">'
output += '<dt>' + key + ':</dt>'
output += '<dd>'
for (var j = 0; j < items.length; j++) {
var item = items[j]
var cName = j == 0 ? 'active' : ''
if (j == 0) {
selectedCache.push(item)
}
output += '<button data-title="' + item + '" class="' + cName + '" value="' + item + '">' + item + '</button> '
}
output += '</dd>'
output += '</dl>'
}
output += '</dl>'
$('#skuSelector').html(output)
}
function getAllKeys(arr) {
var result = []
for (var i = 0; i < arr.length; i++) {
result.push(arr[i].path)
}
return result
}
function powerset(arr) {
var ps = [[]];
for (var i = 0; i < arr.length; i++) {
for (var j = 0, len = ps.length; j < len; j++) {
ps.push(ps[j].concat(arr[i]));
}
}
return ps;
}
function buildResult(items) {
var allKeys = getAllKeys(items)
for (var i = 0; i < allKeys.length; i++) {
var curr = allKeys[i]
var sku = items[i].sku
var price = items[i].price
var values = curr.split(spliter)
var allSets = powerset(values)
for (var j = 0; j < allSets.length; j++) {
var set = allSets[j]
var key = set.join(spliter)
if (res[key]) {
res[key].sku = items[i].sku
res[key].price = items[i].price
res[key].currency = items[i].currency
} else {
res[key] = {
sku: items[i].sku,
price: items[i].price,
currency: items[i].currency
}
}
}
}
}
function trimSpliter(str, spliter) {
var reLeft = new RegExp('^' + spliter + '+', 'g');
var reRight = new RegExp(spliter + '+$', 'g');
var reSpliter = new RegExp(spliter + '+', 'g');
return str.replace(reLeft, '')
.replace(reRight, '')
.replace(reSpliter, spliter)
}
function getSelectedItem() {
var result = []
$('dl[data-type]').each(function () {
var $selected = $(this).find('.active')
if ($selected.length) {
result.push($selected.val())
} else {
result.push('')
}
})
return result
}
function updateStatus(selected) {
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var data = r.result[key]
var hasActive = !!selected[i]
var copy = selected.slice()
for (var j = 0; j < data.length; j++) {
var item = data[j]
if (selected[i] == item) continue
copy[i] = item
var curr = trimSpliter(copy.join(spliter), spliter)
var $item = $('dl').filter('[data-type="' + key + '"]').find('[value="' + item + '"]')
var titleStr = '[' + copy.join('-') + ']'
if (res[curr]) {
$item.removeClass('disabled')
setTitle($item.get(0))
} else {
$item.addClass('disabled').attr('title', titleStr + ' 无此属性搭配')
}
}
}
}
function handleNormalClick($this) {
$this.siblings().removeClass('active')
$this.addClass('active')
}
function handleDisableClick($this) {
var $currAttr = $this.parents('dl').eq(0)
var idx = $currAttr.data('idx')
var type = $currAttr.data('type')
var value = $this.val()
$this.removeClass('disabled')
selectedCache[idx] = value
console.log(selectedCache)
$('dl').not($currAttr).find('button').removeClass('active')
updateStatus(getSelectedItem())
for (var i = 0; i < keys.length; i++) {
var item = keys[i]
var $curr = $('dl[data-type="' + item + '"]')
if (item == type) continue
var $lastSelected = $curr.find('button[value="' + selectedCache[i] + '"]')
if (!$lastSelected.hasClass('disabled')) {
$lastSelected.addClass('active')
updateStatus(getSelectedItem())
}
}
}
function highLightAttr() {
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var $curr = $('dl[data-type="' + key + '"]')
if ($curr.find('.active').length < 1) {
$curr.addClass('hl')
} else {
$curr.removeClass('hl')
}
}
}
function bindEvent() {
$('#skuSelector').undelegate().delegate('button', 'click', function (e) {
var $this = $(this)
var isActive = $this.hasClass('.active')
var isDisable = $this.hasClass('disabled')
if (!isActive) {
handleNormalClick($this)
if (isDisable) {
handleDisableClick($this)
} else {
selectedCache[$this.parents('dl').eq(0).data('idx')] = $this.val()
}
updateStatus(getSelectedItem())
highLightAttr()
showResult()
}
})
$('button').each(function () {
var value = $(this).val()
if (!res[value] && !$(this).hasClass('active')) {
$(this).addClass('disabled')
}
})
}
function showResult() {
var result = getSelectedItem()
var s = []
for (var i = 0; i < result.length; i++) {
var item = result[i];
if (!!item) {
s.push(item)
}
}
if (s.length == keys.length) {
var curr = res[s.join(spliter)]
if (curr && curr.sku) {
cakeProductSkuId = curr.sku
$('#selectedSku').html('<b>' + l('YourChoice') + '</b><br>' + s.join('\u3000-\u3000'))
$('#selectedSkuId').html('<b>' + l('SkuId') + '</b><br>' + curr.sku)
$('#selectedSkuPrice').html('<b>' + l('TotalPrice') + '</b><br>' + curr.price.toFixed(2) + ' ' + curr.currency)
}
}
}
function updateData() {
data = JSON.parse(skuInfo)
init(data)
}
function setTitle(el) {
var title = $(el).data('title');
if (title) $(el).attr('title', title);
}
function setAllTitle() {
$('#skuSelector').find('button').each(setTitle)
}
function initSkuSelector(data) {
res = {}
r = {}
keys = []
selectedCache = []
for (var attr_key in data[0].skus) {
if (!data[0].skus.hasOwnProperty(attr_key)) continue;
keys.push(attr_key)
}
setAllTitle();
r = combineAttr(data, keys)
render(r.result)
buildResult(r.items)
updateStatus(getSelectedItem())
showResult()
bindEvent()
}
initSkuSelector(data)
Loading…
Cancel
Save