نکات «تغییرات مورد نیاز جهت ارتقاء به ASP.NET Core 2.1» در اینجا هم صادق هستند. سمت سرور آن یکی است. سمت کلاینت آن نیز به همین ترتیب. فقط پس از نصب بستهی جدید SignalR، نیاز است یک سطر زیر را نیز تغییر دهید:
از
import { HubConnection } from "@aspnet/signalr-client";
import { HubConnection } from "@aspnet/signalr";
معرفی برنامهی Blazor WASM این مطلب
در این مطلب قصد داریم دقیقا قسمت جزیرهی تعاملی Blazor Server همان برنامهی مطلب قبل را توسط یک جزیرهی تعاملی Blazor WASM بازنویسی کنیم و با نکات و تفاوتهای ویژهی آن آشنا شویم. یعنی زمانیکه صفحهی SSR نمایش جزئیات یک محصول ظاهر میشود، نحوهی رندر و پردازش کامپوننت نمایش محصولات مرتبط و مشابه، اینبار یک جزیرهی تعاملی Blazor WASM باشد. بنابراین قسمت عمدهای از کدهای این دو قسمت یکی است؛ فقط نحوهی دسترسی به سرویسها و محل قرارگیری تعدادی از فایلها، متفاوت خواهد بود.
ایجاد یک پروژهی جدید Blazor WASM تعاملی در دات نت 8
بنابراین در ادامه، در ابتدای کار نیاز است یک پوشهی جدید را برای این پروژه، ایجاد کرده و بجای انتخاب interactivity از نوع Server:
dotnet new blazor --interactivity Server
dotnet new blazor --interactivity WebAssembly
الف) یک پروژهی سمت سرور (برای تامین backend و API و سرویسهای مرتبط)
ب) یک پروژهی سمت کلاینت (برای اجرای مستقیم درون مرورگر کاربر؛ بدون داشتن وابستگی مستقیمی به اجزای برنامهی سمت سرور)
این ساختار، خیلی شبیه به ساختار پروژههای نگارش قبلی Blazor از نوع Hosted Blazor WASM است که در آن، یک پروژهی ASP.NET Core هاست کنندهی پروژهی Blazor WASM وجود دارد و یکی از کارهای اصلی آن، فراهم ساختن Web API مورد استفادهی در پروژهی WASM است.
در حالتیکه نوع تعاملی بودن پروژه را Server انتخاب کنیم (مانند مثال قسمت پنجم)، فایل Program.cs آن به همراه دو تعریف مهم زیر است که امکان تعریف کامپوننتهای تعاملی سمت سرور را میسر میکنند:
// ... builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); // ... app.MapRazorComponents<App>() .AddInteractiveServerRenderMode();
این تعاریف در فایل Program.cs (پروژهی سمت سرور) قالب جدید Blazor WASM به صورت زیر تغییر میکنند تا امکان تعریف کامپوننتهای تعاملی سمت کلاینت از نوع وباسمبلی، میسر شود:
// ... builder.Services.AddRazorComponents() .AddInteractiveWebAssemblyComponents(); // ... app.MapRazorComponents<App>() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(Counter).Assembly);
نیاز به تغییر معماری برنامه جهت کار با جزایر Blazor WASM
همانطور که در قسمت پنجم مشاهده کردیم، تبدیل کردن یک کامپوننت Blazor، به کامپوننتی تعاملی برای اجرای در سمت سرور، بسیار سادهاست؛ فقط کافی است rendermode@ آنرا به InteractiveServer تغییر دهیم تا ... کار کند. اما تبدیل همان کامپوننت نمایش محصولات مرتبط، به یک جزیرهی وباسمبلی، نیاز به تغییرات قابل ملاحظهای را دارد؛ از این لحاظ که اینبار این قسمت قرار است بر روی مرورگر کاربر اجرا شود و نه بر روی سرور. در این حالت دیگر کامپوننت ما دسترسی مستقیمی را به سرویسهای سمت سرور ندارد و برای رسیدن به این مقصود باید از یک Web API در سمت سرور کمک بگیرد و برای کار کردن با آن API در سمت کلاینت، از سرویس HttpClient استفاده کند. به همین جهت، پیاده سازی معماری این روش، نیاز به کار بیشتری را دارد:
همانطور که ملاحظه میکنید، برای فعالسازی یک جزیرهی تعاملی وباسمبلی، نمیتوان کامپوننت RelatedProducts آنرا مستقیما در پروژهی سمت سرور قرار داد و باید آنرا به پروژهی سمت کلاینت منتقل کرد. در ادامه پیاده سازی کامل این پروژه را با توجه به این تغییرات بررسی میکنیم.
مدل برنامه: رکوردی برای ذخیره سازی اطلاعات یک محصول
از این جهت که مدل برنامه (که در قسمت پنجم معرفی شد) در دو پروژهی Client و سرور قابل استفادهاست، به همین جهت مرسوم است یک پروژهی سوم Shared را نیز به جمع دو پروژهی جاری solution اضافه کرد و فایل این مدل را در آن قرار داد. بنابراین این فایل را از پوشهی Models پروژهی سرور به پوشهی Models پروژهی جدید BlazorDemoApp.Shared در مسیر جدید BlazorDemoApp.Shared\Models\Product.cs منتقل میکنیم. مابقی کدهای آن با قسمت پنجم تفاوتی ندارد.
سپس به فایل csproj. پروژهی کلاینت مراجعه کرده و ارجاعی را به پروژهی جدید BlazorDemoApp.Shared اضافه میکنیم:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\BlazorDemoApp.Shared\BlazorDemoApp.Shared.csproj" /> </ItemGroup> </Project>
سرویس برنامه: سرویسی برای بازگشت لیست محصولات
چون Blazor Server و صفحات SSR آن هر دو بر روی سرور اجرا میشوند، از لحاظ دسترسی به اطلاعات و کار با سرویسها، هماهنگی کاملی وجود داشته و میتوان کدهای یکسان و یکدستی را در اینجا بکار گرفت. یعنی هنوز هم همان مسیر قبلی سرویس Services\ProductStore.cs در این پروژهی سمت سرور نیز برقرار است و نیازی به تغییر مسیر آن نیست. البته بدیهی است چون این پروژه جدید است، باید این سرویس را در فایل Program.cs برنامهی سمت سرور به صورت زیر معرفی کرد تا در فایل razor برنامهی آن قابل دسترسی شود:
builder.Services.AddScoped<IProductStore, ProductStore>();
تکمیل فایل Imports.razor_ پروژهی سمت سرور
جهت سهولت کار با برنامه، یک سری مسیر و using را نیاز است به فایل Imports.razor_ پروژهی سمت سرور اضافه کرد:
@using static Microsoft.AspNetCore.Components.Web.RenderMode // ... @using BlazorDemoApp.Client.Components.Store @using BlazorDemoApp.Client.Components
تکمیل صفحهی نمایش لیست محصولات
کدها و مسیر کامپوننت ProductsList.razor، با قسمت پنجم دقیقا یکی است. این صفحه، یک صفحهی SSR بوده و در همان سمت سرور اجرا میشود و دسترسی آن به سرویسهای سمت سرور نیز ساده بوده و همانند قبل است.
تکمیل صفحهی نمایش جزئیات یک محصول
کدها و مسیر کامپوننت ProductDetails.razor، با قسمت پنجم دقیقا یکی است. این صفحه، یک صفحهی SSR بوده و در همان سمت سرور اجرا میشود و دسترسی آن به سرویسهای سمت سرور نیز ساده بوده و همانند قبل است ... البته بجز یک تغییر کوچک:
<RelatedProducts ProductId="ProductId" @rendermode="@InteractiveWebAssembly"/>
تکمیل کامپوننت نمایش لیست محصولات مشابه و مرتبط
پس از این توضیحات، به اصل موضوع این قسمت رسیدیم! کامپوننت سمت سرور RelatedProducts.razor قسمت پنجم ، از آنجا cut شده و به مسیر جدید BlazorDemoApp.Client\Components\Store\RelatedProducts.razor منتقل میشود. یعنی کاملا به پروژهی وباسمبلی منتقل میشود. بنابراین کدهای آن دیگر دسترسی مستقیمی به سرویس دریافت اطلاعات محصولات ندارند و برای اینکار نیاز است در سمت سرور، یک Web API Controller را تدارک ببینیم:
using BlazorDemoApp.Services; using Microsoft.AspNetCore.Mvc; namespace BlazorDemoApp.Controllers; [ApiController] [Route("/api/[controller]")] public class ProductsController : ControllerBase { private readonly IProductStore _store; public ProductsController(IProductStore store) => _store = store; [HttpGet("[action]")] public IActionResult Related([FromQuery] int productId) => Ok(_store.GetRelatedProducts(productId)); }
برای اینکه مسیریابی این کنترلر کار کند، باید به فایل Program.cs برنامه، مراجعه و سطرهای زیر را اضافه کرد:
builder.Services.AddControllers(); // ... app.MapControllers();
یک نکته: همانطور که مشاهده میکنید، در Blazor 8x، امکان استفاده از دو نوع مسیریابی یکپارچه، در یک پروژه وجود دارد؛ یعنی Blazor routing و ASP.NET Core endpoint routing. بنابراین در این پروژهی سمت سرور، هم میتوان صفحات SSR و یا Blazor Server ای داشت که مسیریابی آنها با page@ مشخص میشوند و همزمان کنترلرهای Web API ای را داشت که بر اساس سیستم مسیریابی ASP.NET Core کار میکنند.
بر این اساس در پروژهی سمت کلاینت، کامپوننت RelatedProducts.razor باید با استفاده از سرویس HttpClient، اطلاعات درخواستی را از Web API فوق دریافت و همانند قبل نمایش دهد که تغییرات آن به صورت زیر است:
@using BlazorDemoApp.Shared.Models @inject HttpClient Http <button class="btn btn-outline-secondary" @onclick="LoadRelatedProducts">Related products</button> @if (_loadRelatedProducts) { @if (_relatedProducts == null) { <p>Loading...</p> } else { <div class="mt-3"> @foreach (var item in _relatedProducts) { <a href="/ProductDetails/@item.Id"> <div class="col-sm"> <h5 class="mt-0">@item.Title (@item.Price.ToString("C"))</h5> </div> </a> } </div> } } @code{ private IList<Product>? _relatedProducts; private bool _loadRelatedProducts; [Parameter] public int ProductId { get; set; } private async Task LoadRelatedProducts() { _loadRelatedProducts = true; var uri = $"/api/products/related?productId={ProductId}"; _relatedProducts = await Http.GetFromJsonAsync<IList<Product>>(uri); } }
در ادامه یکبار برنامه را اجرا میکنیم و ... بلافاصله پس از انتخاب صفحهی نمایش جزئیات یک محصول، با خطای زیر مواجه خواهیم شد!
System.InvalidOperationException: Cannot provide a value for property 'Http' on type 'RelatedProducts'. There is no registered service of type 'System.Net.Http.HttpClient'.
اهمیت درنظر داشتن pre-rendering در حالت جزیرههای وباسمبلی
استثنائی را که مشاهده میکنید، به علت pre-rendering سمت سرور این کامپوننت، رخدادهاست.
زمانیکه کامپوننتی را به این نحو رندر میکنیم:
<RelatedProducts ProductId="ProductId" @rendermode="@InteractiveWebAssembly"/>
الف) یکبار در سمت سرور تا HTML حداقل قالب آن، به همراه سایر قسمتهای صفحهی SSR جاری به سمت مرورگر کاربر ارسال شود.
ب) یکبار هم در سمت کلاینت، زمانیکه Blazor WASM بارگذاری شده و فعال میشود.
استثنائی را که مشاهده میکنیم، مربوط به حالت الف است. یعنی زمانیکه برنامهی ASP.NET Core هاست برنامه، سعی میکند کامپوننت RelatedProducts را در سمت سرور رندر کند، اما ... ما سرویس HttpClient را در آن ثبت و فعالسازی نکردهایم. به همین جهت است که عنوان میکند این سرویس را پیدا نکردهاست. برای رفع این مشکل، چندین راهحل وجود دارند که در ادامه آنها را بررسی میکنیم.
راهحل اول: ثبت سرویس HttpClient در سمت سرور
یک راهحل مواجه شدن با مشکل فوق، ثبت سرویس HttpClient در فایل Program.cs برنامهی سمت سرور به صورت زیر است:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://localhost/") });
راهحل دوم: استفاده از polymorphism یا چندریختی
برای اینکار اینترفیسی را طراحی میکنیم که قرارداد نحوهی تامین اطلاعات مورد نیاز کامپوننت RelatedProducts را ارائه میکند. سپس یک پیاده سازی سمت سرور را از آن خواهیم داشت که مستقیما به بانک اطلاعاتی رجوع میکند و همچنین یک پیاده سازی سمت کلاینت را که از HttpClient جهت کار با Web API استفاده خواهد کرد.
از آنجائیکه این قرارداد نیاز است توسط هر دو پروژهی سمت سرور و سمت کلاینت استفاده شود، باید آنرا در پروژهی Shared قرار داد تا بتوان ارجاعاتی از آنرا به هر دو پروژه اضافه کرد؛ برای مثال در فایل BlazorDemoApp.Shared\Data\IProductStore.cs به صورت زیر:
using BlazorDemoApp.Shared.Models; namespace BlazorDemoApp.Shared.Data; public interface IProductStore { IList<Product> GetAllProducts(); Product GetProduct(int id); Task<IList<Product>?> GetRelatedProducts(int productId); }
پیاده سازی سمت سرور این اینترفیس، کاملا مهیا است و فقط نیاز به تغییر زیر را دارد تا با خروجی Task دار هماهنگ شود:
public Task<IList<Product>?> GetRelatedProducts(int productId) { var product = ProductsDataSource.Single(x => x.Id == productId); return Task.FromResult<IList<Product>?>(ProductsDataSource.Where(p => product.Related.Contains(p.Id)) .ToList()); }
[HttpGet("[action]")] public async Task<IActionResult> Related([FromQuery] int productId) => Ok(await _store.GetRelatedProducts(productId));
در ادامه نیاز است یک پیاده سازی سمت کلاینت را نیز از آن تهیه کنیم که در فایل BlazorDemoApp.Client\Data\ClientProductStore.cs درج خواهد شد:
public class ClientProductStore : IProductStore { private readonly HttpClient _httpClient; public ClientProductStore(HttpClient httpClient) => _httpClient = httpClient; public IList<Product> GetAllProducts() => throw new NotImplementedException(); public Product GetProduct(int id) => throw new NotImplementedException(); public Task<IList<Product>?> GetRelatedProducts(int productId) => _httpClient.GetFromJsonAsync<IList<Product>>($"/api/products/related?productId={productId}"); }
builder.Services.AddScoped<IProductStore, ClientProductStore>(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
الف) معرفی سرویس IProductStore بجای HttpClient قبلی
@inject IProductStore ProductStore
private async Task LoadRelatedProducts() { _loadRelatedProducts = true; _relatedProducts = await ProductStore.GetRelatedProducts(ProductId); }
اکنون اگر برنامه را اجرا کنیم، پس از مشاهدهی جزئیات یک محصول، بارگذاری کامپوننت Blazor WASM آن در developer tools مرورگر کاملا مشخص است:
راهحل سوم: استفاده از سرویس PersistentComponentState
با استفاده از سرویس PersistentComponentState میتوان اطلاعات دریافتی از بانکاطلاعاتی را در حین pre-rendering در سمت سرور، به جزایر تعاملی انتقال داد و این روشی است که مایکروسافت برای پیاده سازی مباحث اعتبارسنجی و احراز هویت در Blazor 8x در پیشگرفتهاست. این راهحل را در قسمت بعد بررسی میکنیم.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید: Blazor8x-WebAssembly-Normal.zip
ساخت پوشهی مخصوص template.config.
اولین قدم جهت تبدیل یک پروژهی از پیش موجود، به قالبی جدید، افزودن پوشهی ویژهای به نام template.config. به ریشهی آن است. سپس فایل خالی template.json را با محتوای ذیل به آن اضافه کنید:
{ "author": "VahidN <https://www.dntips.ir/>", "classifications": [ "MVC", ".NET Core", "ASP.NET Core" ], "name": "Empty DNT.Identity project", "identity": "DNT.Identity", "shortName": "dntidentity", "tags": { "language": "C#" }, "sourceName": "ASPNETCoreIdentitySample" }
در اینجا متادیتای تعریف شده شامل موارد ذیل است:
Author: اطلاعات نویسنده است.
Classification: امکان جستجوی بهتر این قالب را فراهم میکند.
Name: توضیحاتی در مورد پروژه.
Identity: نام منحصربفرد پروژه.
ShortName: نامی است که از آن جهت تولید پروژههای جدید، استفاده میشود.
SourceName: مهمترین تنظیم این گروه بوده و نام فضای نام اصلی پروژهاست. زمانیکه پروژهی جدیدی را ایجاد میکنید، این نام به صورت خودکار بر اساس نام جدید انتخابی اصلاح و جایگزین خواهد شد (در تمام پروژههای مربوط به solution جاری).
معرفی قالب تهیه شده به سیستم dotnet
پس از ساخت فایل template.config\template.json. در ریشهی پروژه، اکنون از طریق خط فرمان به ریشهی پروژه وارد شده و دستور ذیل را صادر کنید:
dotnet new -i %~dp0
پس از نصب این پوشه به عنوان یک قالب جدید، یکبار از سیستم وجود آنرا کوئری بگیرید:
dotnet new --list
Templates Short Name Language Tags --------------------------------------------------------------------------------------------------------------- Console Application console [C#], F#, VB Common/Console Class library classlib [C#], F#, VB Common/Library Empty DNT.Identity project dntidentity [C#] MVC/.NET Core/ASP.NET Core
نحوهی ایجاد یک پروژهی جدید بر اساس قالب نصب شده
پس از ساخت این قالب جدید و معرفی آن به سیستم، نحوهی کار با آن به صورت ذیل است:
dotnet new dntidentity -n MyNewProj
معرفی Bit Platform
وب اسمبلی چیست؟
<BlazorMode> ... </BlazorMode> <WebAppDeploymentType> ... </WebAppDeploymentType>
- وجود سیستم Exception handling در سرور و کلاینت (این موضوع به گونه ای بر اساس Best Practiceها پیاده سازی شده که اپلیکیشن را از بروز هر خطایی که بخواهد موجب Crash کردن برنامه شود ایزوله کرده)
- وجود سیستم User Authentication بر اساس JWT که شما در همان ابتدا که از این تمپلیت پروژه جدیدی میسازید صفحات SignIn ، SignUp را خواهید داشت.
- پکیج Bit Blazor UI که بالاتر درمورد آن صحبت کرده ایم از همان ابتدا در TodoTemplate نصب و تنظیم شده تا بتوانید به راحتی صفحات جدید با استفاده از آن بسازید.
- کانفیگ استاندارد Swagger در سمت سرور.
- ارسال ایمیل در روند SignUp.
- وجود خاصیت AutoInject برای سادهسازی تزریق وابستگی ها.
- و بسیاری موراد دیگر که در داکیومنتهای پروژه میتوانید آنهارا ببینید.
- شما میتوانید این پروژه را در گیتهاب مشاهده کنید.
- برای اشکالات یا قابلیت هایی که میخواهید برطرف شود Issue ثبت کنید.
- پروژه را Fork کنید و Star دهید.
- ایشوهایی که وجود دارد را برطرف کنید و Pull Request ارسال کنید.
- برای در جریان بودن از روند توسعه در جلسات برنامه ریزی (Planning Meeting) و گزارشات هفتگی (Standup Meeting ) که همه اینها در Microsoft Teams برگزار میشود شرکت کنید.
public class IdentityUser<TKey> where TKey : IEquatable<TKey> { [ProtectedPersonalData] public virtual string UserName { get; set; } [ProtectedPersonalData] public virtual string Email { get; set; }
روش فعالسازی ذخیره سازی رمزنگاری شدهی اطلاعات شخصی کاربران
اگر برنامهی پیشین خود را به نگارشهای جدیدتر ASP.NET Core Identity ارتقاء داده باشید، احتمالا متوجه وجود یک چنین قابلیتی نشدهاید؛ چون به صورت پیشفرض غیرفعال است. برای فعالسازی آن، میتوان به صورت زیر عمل کرد:
services.AddIdentity<IdentityUser, IdentityRole>(options => { options.Stores.ProtectPersonalData = true; // ... })
ویژگی ProtectedPersonalData چگونه به صورت خودکار پردازش میشود؟
پیشنیاز درک نحوهی پردازش ویژگی ProtectedPersonalData، مطلب «رمزنگاری خودکار فیلدها توسط Entity Framework Core» است. در اینجا نیز دقیقا یک «تبدیلگر مقدار» برای رمزنگاری و رمزگشایی خودکار فیلدهای مزین به ProtectedPersonalData تدارک دیده شدهاست:
private class PersonalDataConverter : ValueConverter<string, string> { public PersonalDataConverter(IPersonalDataProtector protector) : base(s => protector.Protect(s), s => protector.Unprotect(s), default) { } }
سپس در IdentityUserContext تعریف شده و متد OnModelCreating آن، ابتدا بررسی میکند که آیا پیشتر ProtectPersonalData را به true تنظیم کردهاید یا خیر؟ اگر بله، تمام خواصی را که با ویژگی ProtectedPersonalDataAttribute مزین شدهاند، یافته و سپس توسط متد HasConversion به آنها تبدیلگر مقداری از نوع PersonalDataConverter را اضافه میکند:
protected override void OnModelCreating(ModelBuilder builder) { var encryptPersonalData = storeOptions?.ProtectPersonalData ?? false; builder.Entity<TUser>(b => { if (encryptPersonalData) { converter = new PersonalDataConverter(this.GetService<IPersonalDataProtector>()); var personalDataProps = typeof(TUser).GetProperties().Where( prop => Attribute.IsDefined(prop, typeof(ProtectedPersonalDataAttribute))); foreach (var p in personalDataProps) { if (p.PropertyType != typeof(string)) { throw new InvalidOperationException(Resources.CanOnlyProtectStrings); } b.Property(typeof(string), p.Name).HasConversion(converter); } } // ...
اگر در کدهای فوق، به جائیکه new PersonalDataConverter نوشته شده دقت کنید؛ یک سرویس از نوع IPersonalDataProtector را دریافت میکند که توسط آن دو متد Protect و Unprotect به کلاس خصوصی PersonalDataConverter تزریق میشوند:
public interface IPersonalDataProtector { string Protect(string data); string Unprotect(string data); }
تاثیر فعالسازی ProtectPersonalData بر روی سایر سرویسهای ASP.NET Core Identity
اگر options.Stores.ProtectPersonalData را به true تنظیم کنید، سرویس UserManager اینبار انتظار خواهد داشت که IUserStore ای که به آن تزریق میشود، از نوع جدید IProtectedUserStore باشد؛ در غیراینصورت یک استثناء را صادر میکند:
if (Options.Stores.ProtectPersonalData) { if (!(Store is IProtectedUserStore<TUser>)) { throw new InvalidOperationException(Resources.StoreNotIProtectedUserStore); } if (services.GetService<ILookupProtector>() == null) { throw new InvalidOperationException(Resources.NoPersonalDataProtector); } }
public class UserStore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> : ...
همچنین تمام متدهای سرویس UserManager که با خواص مزین شدهی به [ProtectedPersonalData] کار میکنند، جهت استفادهی از متد ProtectPersonalData به روز رسانی شدهاند:
public virtual async Task UpdateNormalizedUserNameAsync(TUser user) { var normalizedName = NormalizeName(await GetUserNameAsync(user)); normalizedName = ProtectPersonalData(normalizedName); await Store.SetNormalizedUserNameAsync(user, normalizedName, CancellationToken); }
چگونه میتوان بجای DefaultPersonalDataProtector پیشفرض، از یک نمونهی سفارشی استفاده کرد؟
برای انجام اینکار نیاز است ILookupProtector خودتان را نوشته و به سیستم معرفی کنید. یک نمونه از پیاده سازی سفارشی آنرا در پروژهی AspNetCoreIdentityEncryption میتوانید مشاهده نمائید.
https://github.com/MehdiSaeedifar/IrisStore
همچنین نمونهی آنلاین آنرا میتوانید در فروشگاه آیریس مشاهده کنید.
در ادامه برخی از قابلیتهای این سیستم را مشاهده میکنید:
جست و جو با قابلیت دسته بندی نتایج
به هنگام جست و جو، لیستی از موارد پیشنهادی به صورت دسته بندی شده نمایش داده میشود.
جست و جوی پیشرفته کالاها
جست و جو بر اساس قیمت، گروه، کلمات کلیدی و مرتب سازی نتایج انجام میگیرد. همچنین نتایج جست و جو بدون رفرش شدن صفحه و به صورت AJAX ای به همراه تغییر URL صفحه صورت میگیرد.
نمایش نمودار تغییرات قیمت
امکان نمایش نمودار تغییرات قیمت کالا در بازهی زمانی نیز پیش بینی شده است.
ویرایش اطلاعات به صورت inline
امکان ویرایش قیمت و تاریخ به صورت inline وجود دارد.
مدیریت تصاویر کالا
در این قسمت امکان آپلود همزمان چندین فایل به همراه پیش نمایش آنها وجود دارد. همچنین امکان کشیدن و رها کردن برای تغییر ترتیب چیدمان عکسها نیز مهیا است.( تصویر اول به عنوان کاور کالا در نظر گرفته میشود.)
قابلیتهای دیگر:
- مدیریت تصاویر اسلایدشو و تغییر ترتیب آنها از طریق کشیدن و رها کردن (drag & drop)
- تعریف برگه و تغییر ترتیب نمایش آنها از طریق کشیدن و رها کردن
- امکان ارسال پست
- تعریف دسته بندی
- مدیریت کاربران
- تعریف تنظیمات سایت
- نمایش کالا و پستهای مشابه
کارهایی که باید انجام شود:
- پیاده سازی سبد خرید و خرید آنلاین
تصویر پنل مدیریت
تصویر صفحهی اصلی:
همچنین به راحتی میتوان با طراحی قالب جدیدی، از این سیستم برای کاری غیر از فروشگاه اینترنتی استفاده کرد؛ سایتهای زیر نمونههای آنلاین دیگری از این سیستم هستند:
- http://www.petrapars.ir
- http://www.ava-tarh.ir
در نهایت فهرستی از کتاب خانهها و فناوریهای استفاده شده و همچنین مقالات مرتبط با این پروژه را قرار دادهام.
کتابخانهها و فریم ورکهای سمت سرور:
فناوری یا کتابخانه | توضیحات | مقالات مرتبط |
Bootstrap 3.x | فریم ورک پایه ای css سایت | - Bootstrap 3 RTL Theme - Twitter Bootstrap -سازگارسازی کلاسهای اعتبارسنجی Twitter Bootstrap 3 با فرمهای ASP.NET MVC -ساخت قالبهای نمایشی و ادیتور دکمه سه وضعیتی سازگار با Twitter bootstrap در ASP.NET MVC -نمایش اخطارها و پیامهای بوت استرپ به کمک TempData در ASP.NET MVC |
AdminLTE | قالب مدیریت سایت | - نسخه راستچین شده AdminLTE 2.2.1 |
Animate.css | انیمیشنهای css3 سایت | |
Font Awesome | پک آیکونهای برداری | |
Awesome Bootstrap Checkbox | زیبا سازی چک باکس ها | |
فونت فارسی وزیر | قلم فارسی | |
لطفا برای طرح سؤالات و پیشنهادات خود و جهت مدیریت بهتر آنها، از قسمت اختصاصی این پروژه در سایت استفاده نمائید.