mirror of https://github.com/EasyAbp/EShop.git
29 changed files with 685 additions and 1 deletions
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products.Dtos |
|||
{ |
|||
public class UpdateProductSkuDto |
|||
{ |
|||
[DisplayName("ProductSkuCurrency")] |
|||
public string Currency { get; set; } |
|||
|
|||
[DisplayName("ProductSkuOriginalPrice")] |
|||
public decimal OriginalPrice { get; set; } |
|||
|
|||
[DisplayName("ProductSkuPrice")] |
|||
public decimal Price { get; set; } |
|||
|
|||
[DisplayName("ProductSkuInventory")] |
|||
public int Inventory { get; set; } |
|||
|
|||
[DisplayName("ProductSkuOrderMinQuantity")] |
|||
public int OrderMinQuantity { get; set; } |
|||
|
|||
[DisplayName("ProductSkuProductDetailId")] |
|||
public Guid? ProductDetailId { get; set; } |
|||
} |
|||
|
|||
public class CreateProductSkuDto : UpdateProductSkuDto |
|||
{ |
|||
[Required] |
|||
[DisplayName("ProductSkuSerializedAttributeOptionIds")] |
|||
public string SerializedAttributeOptionIds { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using Volo.Abp; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
public class ProductSkuDuplicatedException : BusinessException |
|||
{ |
|||
public ProductSkuDuplicatedException(Guid productId, string serializedAttributeOptionIds) : base( |
|||
message: $"Sku {serializedAttributeOptionIds} is duplicate for the product {productId}") |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
public interface ISerializedAttributeOptionIdsFormatter |
|||
{ |
|||
Task<string> ParseAsync(string serializedAttributeOptionIds); |
|||
|
|||
Task<string> ParseAsync(IEnumerable<Guid> attributeOptionIds); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Products.Products |
|||
{ |
|||
public class SerializedAttributeOptionIdsFormatter : ISerializedAttributeOptionIdsFormatter, ITransientDependency |
|||
{ |
|||
public async Task<string> ParseAsync(string serializedAttributeOptionIds) |
|||
{ |
|||
return await ParseAsync(JsonConvert.DeserializeObject<IEnumerable<Guid>>(serializedAttributeOptionIds)); |
|||
} |
|||
|
|||
public Task<string> ParseAsync(IEnumerable<Guid> attributeOptionIds) |
|||
{ |
|||
return Task.FromResult(JsonConvert.SerializeObject(attributeOptionIds.OrderBy(x => x))); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
@page |
|||
@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
|||
@model EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.CreateModalModel |
|||
@{ |
|||
Layout = null; |
|||
} |
|||
<abp-dynamic-form abp-model="ProductSku" data-ajaxForm="true" asp-page="CreateModal"> |
|||
<abp-modal> |
|||
<abp-modal-header title="@L["CreateProductSku"].Value"></abp-modal-header> |
|||
<abp-modal-body> |
|||
<abp-form-content/> |
|||
@foreach (var attr in Model.Attributes) |
|||
{ |
|||
<abp-select asp-for="@Model.SelectedAttributeOptionIdDict[attr.Key]" asp-items="@attr.Value" label="@L["ProductAttribute"].Value - @attr.Key" /> |
|||
} |
|||
<abp-input asp-for="StoreId" /> |
|||
<abp-input asp-for="ProductId" /> |
|||
</abp-modal-body> |
|||
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer> |
|||
</abp-modal> |
|||
</abp-dynamic-form> |
|||
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Products.Categories; |
|||
using EasyAbp.EShop.Products.ProductDetails; |
|||
using EasyAbp.EShop.Products.ProductDetails.Dtos; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using EasyAbp.EShop.Products.Products.Dtos; |
|||
using EasyAbp.EShop.Products.ProductTypes; |
|||
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.Product.ViewModels; |
|||
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Rendering; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku |
|||
{ |
|||
public class CreateModalModel : ProductsPageModel |
|||
{ |
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid StoreId { get; set; } |
|||
|
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid ProductId { get; set; } |
|||
|
|||
[BindProperty] |
|||
public CreateEditProductSkuViewModel ProductSku { get; set; } |
|||
|
|||
[BindProperty] |
|||
public Dictionary<string, Guid> SelectedAttributeOptionIdDict { get; set; } |
|||
|
|||
public Dictionary<string, ICollection<SelectListItem>> Attributes { get; set; } |
|||
|
|||
private readonly IProductAppService _productAppService; |
|||
|
|||
public CreateModalModel(IProductAppService productAppService) |
|||
{ |
|||
_productAppService = productAppService; |
|||
} |
|||
|
|||
public virtual async Task OnGetAsync() |
|||
{ |
|||
var product = await _productAppService.GetAsync(ProductId); |
|||
|
|||
Attributes = new Dictionary<string, ICollection<SelectListItem>>(); |
|||
|
|||
foreach (var attribute in product.ProductAttributes.ToList()) |
|||
{ |
|||
Attributes.Add(attribute.DisplayName, |
|||
attribute.ProductAttributeOptions |
|||
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList()); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task<IActionResult> OnPostAsync() |
|||
{ |
|||
var createDto = ObjectMapper.Map<CreateEditProductSkuViewModel, CreateProductSkuDto>(ProductSku); |
|||
|
|||
createDto.SerializedAttributeOptionIds = JsonConvert.SerializeObject(SelectedAttributeOptionIdDict.Values); |
|||
|
|||
await _productAppService.CreateSkuAsync(ProductId, StoreId, createDto); |
|||
|
|||
return NoContent(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
@page |
|||
@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
|||
@model EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.EditModalModel |
|||
@{ |
|||
Layout = null; |
|||
} |
|||
<abp-dynamic-form abp-model="ProductSku" data-ajaxForm="true" asp-page="EditModal"> |
|||
<abp-modal> |
|||
<abp-modal-header title="@L["EditProductSku"].Value"></abp-modal-header> |
|||
<abp-modal-body> |
|||
<abp-input asp-for="Id" /> |
|||
<abp-form-content /> |
|||
</abp-modal-body> |
|||
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer> |
|||
</abp-modal> |
|||
</abp-dynamic-form> |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using EasyAbp.EShop.Products.Products.Dtos; |
|||
using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.Rendering; |
|||
|
|||
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku |
|||
{ |
|||
public class EditModalModel : ProductsPageModel |
|||
{ |
|||
[HiddenInput] |
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid Id { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid StoreId { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid ProductId { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid ProductSkuId { get; set; } |
|||
|
|||
[BindProperty] |
|||
public CreateEditProductSkuViewModel ProductSku { get; set; } |
|||
|
|||
private readonly IProductAppService _productAppService; |
|||
|
|||
public EditModalModel(IProductAppService productAppService) |
|||
{ |
|||
_productAppService = productAppService; |
|||
} |
|||
|
|||
public virtual async Task OnGetAsync() |
|||
{ |
|||
var product = await _productAppService.GetAsync(ProductId); |
|||
|
|||
ProductSku = |
|||
ObjectMapper.Map<ProductSkuDto, CreateEditProductSkuViewModel>( |
|||
product.ProductSkus.Single(x => x.Id == ProductSkuId)); |
|||
} |
|||
|
|||
public virtual async Task<IActionResult> OnPostAsync() |
|||
{ |
|||
await _productAppService.UpdateSkuAsync(ProductId, ProductSkuId, StoreId, |
|||
ObjectMapper.Map<CreateEditProductSkuViewModel, UpdateProductSkuDto>(ProductSku)); |
|||
|
|||
return NoContent(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
@page |
|||
@using EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
|||
@inherits EasyAbp.EShop.Products.Web.Pages.ProductsPage |
|||
@model IndexModel |
|||
@inject IPageLayout PageLayout |
|||
@{ |
|||
PageLayout.Content.Title = L["ProductSku"].Value; |
|||
PageLayout.Content.BreadCrumb.Add(L["Menu:Product"].Value); |
|||
PageLayout.Content.MenuItemName = "Product"; |
|||
} |
|||
|
|||
@section scripts |
|||
{ |
|||
<abp-script src="/Pages/EShop/Products/Products/ProductSku/index.js" /> |
|||
} |
|||
@section styles |
|||
{ |
|||
<abp-style src="/Pages/EShop/Products/Products/ProductSku/index.css"/> |
|||
} |
|||
<script> |
|||
let storeId = '@Model.StoreId'; |
|||
let productId = '@Model.ProductId'; |
|||
</script> |
|||
|
|||
<abp-card> |
|||
<abp-card-header> |
|||
<abp-row> |
|||
<abp-column size-md="_6"> |
|||
<abp-card-title>@L["ProductSku"] - @Model.ProductDisplayName</abp-card-title> |
|||
</abp-column> |
|||
<abp-column size-md="_6" class="text-right"> |
|||
<abp-button id="NewProductSkuButton" |
|||
text="@L["CreateProductSku"].Value" |
|||
icon="plus" |
|||
button-type="Primary" /> |
|||
</abp-column> |
|||
</abp-row> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<abp-table striped-rows="true" id="ProductSkuTable" class="nowrap"> |
|||
<thead> |
|||
<tr> |
|||
<th>@L["Actions"]</th> |
|||
<th>@L["ProductSkuContentDescription"]</th> |
|||
<th>@L["ProductSkuPrice"]</th> |
|||
<th>@L["ProductSkuInventory"]</th> |
|||
</tr> |
|||
</thead> |
|||
</abp-table> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using EasyAbp.EShop.Stores.Stores; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
|
|||
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku |
|||
{ |
|||
public class IndexModel : ProductsPageModel |
|||
{ |
|||
private readonly IProductAppService _productAppService; |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid StoreId { get; set; } |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public Guid ProductId { get; set; } |
|||
|
|||
public string ProductDisplayName { get; set; } |
|||
|
|||
public IndexModel(IProductAppService productAppService) |
|||
{ |
|||
_productAppService = productAppService; |
|||
} |
|||
|
|||
public virtual async Task OnGetAsync() |
|||
{ |
|||
ProductDisplayName = (await _productAppService.GetAsync(ProductId)).DisplayName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
|
|||
namespace EasyAbp.EShop.Products.Web.Pages.EShop.Products.Products.ProductSku.ViewModels |
|||
{ |
|||
public class CreateEditProductSkuViewModel |
|||
{ |
|||
[Required] |
|||
[Display(Name = "ProductSkuCurrency")] |
|||
public string Currency { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "ProductSkuPrice")] |
|||
public decimal Price { get; set; } |
|||
|
|||
[Display(Name = "ProductSkuOriginalPrice")] |
|||
public decimal OriginalPrice { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "ProductSkuInventory")] |
|||
public int Inventory { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "ProductSkuOrderMinQuantity")] |
|||
public int OrderMinQuantity { get; set; } |
|||
|
|||
[HiddenInput] |
|||
[Display(Name = "ProductSkuProductDetailId")] |
|||
public Guid? ProductDetailId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
$(function () { |
|||
|
|||
var l = abp.localization.getResource('Products'); |
|||
|
|||
var service = easyAbp.eShop.products.products.product; |
|||
var createModal = new abp.ModalManager(abp.appPath + 'EShop/Products/Products/ProductSku/CreateModal'); |
|||
var editModal = new abp.ModalManager(abp.appPath + 'EShop/Products/Products/ProductSku/EditModal'); |
|||
|
|||
var dataTable = $('#ProductSkuTable').DataTable(abp.libs.datatables.normalizeConfiguration({ |
|||
processing: true, |
|||
autoWidth: false, |
|||
scrollCollapse: true, |
|||
order: [[1, "asc"]], |
|||
ajax: function (requestData, callback, settings) { |
|||
if (callback) { |
|||
service.get(productId).then(function (result) { |
|||
callback({ |
|||
recordsTotal: result.productSkus.length, |
|||
recordsFiltered: result.productSkus.length, |
|||
data: fillProductSkusContentDescription(result).productSkus |
|||
}); |
|||
}); |
|||
} |
|||
}, |
|||
columnDefs: [ |
|||
{ |
|||
rowAction: { |
|||
items: |
|||
[ |
|||
{ |
|||
text: l('Edit'), |
|||
action: function (data) { |
|||
editModal.open({ productId: productId, productSkuId: data.record.id, storeId: storeId }); |
|||
} |
|||
}, |
|||
{ |
|||
text: l('Delete'), |
|||
confirmMessage: function (data) { |
|||
return l('ProductDeletionConfirmationMessage', data.record.id); |
|||
}, |
|||
action: function (data) { |
|||
service.deleteSku(productId, data.record.id, storeId) |
|||
.then(function () { |
|||
abp.notify.info(l('SuccessfullyDeleted')); |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ data: "contentDescription" }, |
|||
{ data: "price" }, |
|||
{ data: "inventory" }, |
|||
] |
|||
})); |
|||
|
|||
createModal.onResult(function () { |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
|
|||
editModal.onResult(function () { |
|||
dataTable.ajax.reload(); |
|||
}); |
|||
|
|||
$('#NewProductSkuButton').click(function (e) { |
|||
e.preventDefault(); |
|||
createModal.open({ storeId: storeId, productId: productId }); |
|||
}); |
|||
|
|||
function fillProductSkusContentDescription(product) { |
|||
let attributeOptionNames = []; |
|||
for (let i in product.productAttributes) { |
|||
let attribute = product.productAttributes[i]; |
|||
for (let j in attribute.productAttributeOptions) { |
|||
let option = attribute.productAttributeOptions[j]; |
|||
attributeOptionNames[option.id] = option.displayName; |
|||
} |
|||
} |
|||
for (let i in product.productSkus) { |
|||
let sku = product.productSkus[i]; |
|||
let options = []; |
|||
let attributeOptionIds = JSON.parse(sku.serializedAttributeOptionIds); |
|||
for (let j in attributeOptionIds) { |
|||
let optionId = attributeOptionIds[j]; |
|||
options.push(attributeOptionNames[optionId]); |
|||
} |
|||
sku.contentDescription = options.join(','); |
|||
} |
|||
return product; |
|||
} |
|||
}); |
|||
Loading…
Reference in new issue