smartadmin.core.urfは、ドメイン駆動型の設計原則とパターンに基づいて、完全でモジュール式および階層化されたソフトウェアアーキテクチャを提供します。また、このアーキテクチャを実装するために必要なインフラストラクチャを提供し、ビジネス需要の中核に焦点を当て、重複コーディングを削減し、ジュニアプログラマーが専門的で美しいWebアプリケーションを開発できるようにします
ドメイン駆動型設計(DDD)は、ソフトウェアコード(クラス名、クラスメソッド、クラス変数)の構造と言語がビジネスドメインと一致するという概念です。たとえば、ソフトウェアがローンアプリケーションを処理する場合、LoanApplicationや顧客などのクラスがあり、AcceptOfferや撤回などの方法があります。 DDDは、実装を進化するモデルに接続します。ドメイン駆動型のデザインは、次の目標に基づいています。
- プロジェクトの主要な焦点をコアドメインとドメインロジックに焦点を当てます。
- ドメインのモデルに基づいて複雑なデザインをベースにします。
- 技術とドメインの専門家との間の創造的なコラボレーションを開始して、特定のドメインの問題に対処する概念モデルを繰り返し改善します。
ユーザー名:デモパスワード: 123456デモ(https://admin.i247365.net/)
あなたがそれが好きなら、私に星をください。すべての星は、更新を続けるように奨励する動機です。
smartadmin.core.urf DDD設計パターンに従って、アプリケーションの4層モデルを実装する

ドメインレイヤー
- エンティティ:( smartadmin.entity.csproj)
- 基本クラスの「エンティティ」を継承し、作成時間、最終変更時間など、必要な監査クラスを追加します。
- 主キーが必要です。好ましくはGruid(複合プライマリキーは推奨されません)が、このプロジェクトでは増分INTタイプを使用しています
- フィールドはあまりにも冗長であってはなりません、あなたは協会関係を定義することができます
- フィールドのプロパティとメソッドには、可能な限り仮想キーワードを使用します。一部のORMおよび動的プロキシツールが必要です
アプリケーションレイヤー
インフラストラクチャー
- Visual Studio .NET 2019
- .NET 5.0.1
- sql server(localdb)
- include = "automapper" version = "10.1.1"
- include = "automapper.extensions.microsoft.dependencyinjection" version = "8.1.1"
- include = "dotnetcore.cap" version = "3.1.2"
- include = "dotnetcore.cap.dashboard" version = "3.1.2"
- include = "dotnetcore.cap.rabbitmq"バージョン= "3.1.2"
- include = "dotnetcore.cap.sqlserver" version = "3.1.2"
- include = "dotnetcore.npoi"バージョン= "1.2.3"
- include = "mapster" version = "7.1.5"
- include = "mediatr"バージョン= "9.0.0"
- include = "mediatr.extensions.microsoft.dependencyinjection" version = "9.0.0"
- include = "microsoft.aspnetcore.authentication.jwtbearer"バージョン= "5.0.3"
- include = "microsoft.aspnetcore.diagnostics.entityframeworkcore"バージョン= "5.0.3"
- include = "microsoft.aspnetcore.identity.entityframeworkcore"バージョン= "5.0.3"
- include = "microsoft.aspnetcore.identity.ui"バージョン= "5.0.3"
- include = "microsoft.aspnetcore.mvc.razor.runtimeCompilation"バージョン= "5.0.3"
- include = "microsoft.aspnetcore.mvc.viewfeatures" version = "2.2.0"
- include = "microsoft.aspnetcore.signalr"バージョン= "1.1.0"
- include = "microsoft.entityframeworkcore.design" version = "5.0.3"
- include = "microsoft.entityframeworkcore.sqlserver"バージョン= "5.0.3"
- include = "microsoft.entityframeworkcore.toors" version = "5.0.3">
- include = "microsoft.visualstudio.web.codegeneration.design" version = "5.0.2"
- include = "nlog"バージョン= "4.7.8"
- include = "nlog.extensions.logging"バージョン= "1.7.1"
- include = "nlog.web.aspnetcore"バージョン= "4.11.0"
- include = "swashbuckle.aspnetcore"バージョン= "6.1.0"
- include = "system.data.sqlclient"バージョン= "4.8.2"
- include = "system.linq.dynamic.core" version = "1.2.8"
簡単な要件から始めます
会社オブジェクトを使用して、CRUD、インポート、エクスポート機能を開発します
smartadmin.entity.csproj>モデルnew company.csクラス
//Note: define the best practices of entity objects, inherit the base class, use the virtual keyword, and define every attribute, name, type, length, verification rule, index, default value, etc. as much as possible
namespace SmartAdmin . Data . Models
{
public partial class Company : URF . Core . EF . Trackable . Entity
{
[ Display ( Name = "企业名称" , Description = "归属企业名称" ) ]
[ MaxLength ( 50 ) ]
[ Required ]
//[Index(IsUnique = true)]
public virtual string Name { get ; set ; }
[ Display ( Name = "组织代码" , Description = "组织代码" ) ]
[ MaxLength ( 12 ) ]
//[Index(IsUnique = true)]
[ Required ]
public virtual string Code { get ; set ; }
[ Display ( Name = "地址" , Description = "地址" ) ]
[ MaxLength ( 128 ) ]
[ DefaultValue ( "-" ) ]
public virtual string Address { get ; set ; }
[ Display ( Name = "联系人" , Description = "联系人" ) ]
[ MaxLength ( 12 ) ]
public virtual string Contact { get ; set ; }
[ Display ( Name = "联系电话" , Description = "联系电话" ) ]
[ MaxLength ( 20 ) ]
public virtual string PhoneNumber { get ; set ; }
[ Display ( Name = "注册日期" , Description = "注册日期" ) ]
[ DefaultValue ( "now" ) ]
public virtual DateTime RegisterDate { get ; set ; }
}
}
//在 SmartAdmin.Data.csproj 项目 SmartDbContext.cs 添加
public virtual DbSet < Company > Companies { get ; set ; }プロジェクトsmartadmin.service.csprojは、 icompanyservice.cs 、 companyservice.csのビジネス要件とユースケースを実装する
//ICompany.cs
//Create methods based on actual business use cases, the default CRUD, additions, deletions, and changes do not need to be defined
namespace SmartAdmin . Service
{
// Example: extending IService<TEntity> and/or ITrackableRepository<TEntity>, scope: ICustomerService
public interface ICompanyService : IService < Company >
{
// Example: adding synchronous Single method, scope: ICustomerService
Company Single ( Expression < Func < Company , bool >> predicate ) ;
Task ImportDataTableAsync ( DataTable datatable ) ;
Task < Stream > ExportExcelAsync ( string filterRules = "" , string sort = "Id" , string order = "asc" ) ;
}
} // implementation of the interface method
namespace SmartAdmin . Service
{
public class CompanyService : Service < Company > , ICompanyService
{
private readonly IDataTableImportMappingService mappingservice ;
private readonly ILogger < CompanyService > logger ;
public CompanyService (
IDataTableImportMappingService mappingservice ,
ILogger < CompanyService > logger ,
ITrackableRepository < Company > repository ) : base ( repository )
{
this . mappingservice = mappingservice ;
this . logger = logger ;
}
public async Task < Stream > ExportExcelAsync ( string filterRules = "" , string sort = "Id" , string order = "asc" )
{
var filters = PredicateBuilder . FromFilter < Company > ( filterRules ) ;
var expcolopts = await this . mappingservice . Queryable ( )
. Where ( x => x . EntitySetName == "Company" )
. Select ( x => new ExpColumnOpts ( )
{
EntitySetName = x . EntitySetName ,
FieldName = x . FieldName ,
IgnoredColumn = x . IgnoredColumn ,
SourceFieldName = x . SourceFieldName
} ) . ToArrayAsync ( ) ;
var works = ( await this . Query ( filters ) . OrderBy ( n => n . OrderBy ( sort , order ) ) . SelectAsync ( ) ) . ToList ( ) ;
var datarows = works . Select ( n => new
{
Id = n . Id ,
Name = n . Name ,
Code = n . Code ,
Address = n . Address ,
Contect = n . Contect ,
PhoneNumber = n . PhoneNumber ,
RegisterDate = n . RegisterDate . ToString ( "yyyy-MM-dd HH:mm:ss" )
} ) . ToList ( ) ;
return await NPOIHelper . ExportExcelAsync ( "Company" , datarows , expcolopts ) ;
}
public async Task ImportDataTableAsync ( DataTable datatable )
{
var mapping = await this . mappingservice . Queryable ( )
. Where ( x => x . EntitySetName == "Company" &&
( x . IsEnabled == true || ( x . IsEnabled == false && x . DefaultValue != null ) )
) . ToListAsync ( ) ;
if ( mapping . Count == 0 )
{
throw new NullReferenceException ( "没有找到Work对象的Excel导入配置信息,请执行[系统管理/Excel导入配置]" ) ;
}
foreach ( DataRow row in datatable . Rows )
{
var requiredfield = mapping . Where ( x => x . IsRequired == true && x . IsEnabled == true && x . DefaultValue == null ) . FirstOrDefault ( ) ?. SourceFieldName ;
if ( requiredfield != null || ! row . IsNull ( requiredfield ) )
{
var item = new Company ( ) ;
foreach ( var field in mapping )
{
var defval = field . DefaultValue ;
var contain = datatable . Columns . Contains ( field . SourceFieldName ?? "" ) ;
if ( contain && ! row . IsNull ( field . SourceFieldName ) )
{
var worktype = item . GetType ( ) ;
var propertyInfo = worktype . GetProperty ( field . FieldName ) ;
var safetype = Nullable . GetUnderlyingType ( propertyInfo . PropertyType ) ?? propertyInfo . PropertyType ;
var safeValue = ( row [ field . SourceFieldName ] == null ) ? null : Convert . ChangeType ( row [ field . SourceFieldName ] , safetype ) ;
propertyInfo . SetValue ( item , safeValue , null ) ;
}
else if ( ! string . IsNullOrEmpty ( defval ) )
{
var worktype = item . GetType ( ) ;
var propertyInfo = worktype . GetProperty ( field . FieldName ) ;
if ( string . Equals ( defval , "now" , StringComparison . OrdinalIgnoreCase ) && ( propertyInfo . PropertyType == typeof ( DateTime ) || propertyInfo . PropertyType == typeof ( Nullable < DateTime > ) ) )
{
var safetype = Nullable . GetUnderlyingType ( propertyInfo . PropertyType ) ?? propertyInfo . PropertyType ;
var safeValue = Convert . ChangeType ( DateTime . Now , safetype ) ;
propertyInfo . SetValue ( item , safeValue , null ) ;
}
else if ( string . Equals ( defval , "guid" , StringComparison . OrdinalIgnoreCase ) )
{
propertyInfo . SetValue ( item , Guid . NewGuid ( ) . ToString ( ) , null ) ;
}
else if ( string . Equals ( defval , "user" , StringComparison . OrdinalIgnoreCase ) )
{
propertyInfo . SetValue ( item , "" , null ) ;
}
else
{
var safetype = Nullable . GetUnderlyingType ( propertyInfo . PropertyType ) ?? propertyInfo . PropertyType ;
var safeValue = Convert . ChangeType ( defval , safetype ) ;
propertyInfo . SetValue ( item , safeValue , null ) ;
}
}
}
this . Insert ( item ) ;
}
}
}
// Example, adding synchronous Single method
public Company Single ( Expression < Func < Company , bool >> predicate )
{
return this . Repository . Queryable ( ) . Single ( predicate ) ;
}
}
}MVCコントローラー
namespace SmartAdmin . WebUI . Controllers
{
public class CompaniesController : Controller
{
private readonly ICompanyService companyService ;
private readonly IUnitOfWork unitOfWork ;
private readonly ILogger < CompaniesController > _logger ;
private readonly IWebHostEnvironment _webHostEnvironment ;
public CompaniesController ( ICompanyService companyService ,
IUnitOfWork unitOfWork ,
IWebHostEnvironment webHostEnvironment ,
ILogger < CompaniesController > logger )
{
this . companyService = companyService ;
this . unitOfWork = unitOfWork ;
this . _logger = logger ;
this . _webHostEnvironment = webHostEnvironment ;
}
// GET: Companies
public IActionResult Index ( ) => View ( ) ;
//datagrid 数据源
public async Task < JsonResult > GetData ( int page = 1 , int rows = 10 , string sort = "Id" , string order = "asc" , string filterRules = "" )
{
try
{
var filters = PredicateBuilder . FromFilter < Company > ( filterRules ) ;
var total = await this . companyService
. Query ( filters )
. AsNoTracking ( )
. CountAsync ( )
;
var pagerows = ( await this . companyService
. Query ( filters )
. AsNoTracking ( )
. OrderBy ( n => n . OrderBy ( sort , order ) )
. Skip ( page - 1 ) . Take ( rows )
. SelectAsync ( ) )
. Select ( n => new
{
Id = n . Id ,
Name = n . Name ,
Code = n . Code ,
Address = n . Address ,
Contect = n . Contect ,
PhoneNumber = n . PhoneNumber ,
RegisterDate = n . RegisterDate . ToString ( "yyyy-MM-dd HH:mm:ss" )
} ) . ToList ( ) ;
var pagelist = new { total = total , rows = pagerows } ;
return Json ( pagelist ) ;
}
catch ( Exception e ) {
throw e ;
}
}
//编辑
[ HttpPost ]
[ ValidateAntiForgeryToken ]
public async Task < JsonResult > Edit ( Company company )
{
if ( ModelState . IsValid )
{
try
{
this . companyService . Update ( company ) ;
var result = await this . unitOfWork . SaveChangesAsync ( ) ;
return Json ( new { success = true , result = result } ) ;
}
catch ( Exception e )
{
return Json ( new { success = false , err = e . GetBaseException ( ) . Message } ) ;
}
}
else
{
var modelStateErrors = string . Join ( " , ", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage)));
return Json ( new { success = false , err = modelStateErrors } ) ;
//DisplayErrorMessage(modelStateErrors);
}
//return View(work);
}
//新建
[ HttpPost ]
[ ValidateAntiForgeryToken ]
public async Task < JsonResult > Create ( [ Bind ( "Name,Code,Address,Contect,PhoneNumber,RegisterDate" ) ] Company company )
{
if ( ModelState . IsValid )
{
try
{
this . companyService . Insert ( company ) ;
await this . unitOfWork . SaveChangesAsync ( ) ;
return Json ( new { success = true } ) ;
}
catch ( Exception e )
{
return Json ( new { success = false , err = e . GetBaseException ( ) . Message } ) ;
}
//DisplaySuccessMessage("Has update a Work record");
//return RedirectToAction("Index");
}
else
{
var modelStateErrors = string . Join ( " , ", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage)));
return Json ( new { success = false , err = modelStateErrors } ) ;
//DisplayErrorMessage(modelStateErrors);
}
//return View(work);
}
//删除当前记录
//GET: Companies/Delete/:id
[ HttpGet ]
public async Task < JsonResult > Delete ( int id )
{
try
{
await this . companyService . DeleteAsync ( id ) ;
await this . unitOfWork . SaveChangesAsync ( ) ;
return Json ( new { success = true } ) ;
}
catch ( Exception e )
{
return Json ( new { success = false , err = e . GetBaseException ( ) . Message } ) ;
}
}
//删除选中的记录
[ HttpPost ]
public async Task < JsonResult > DeleteChecked ( int [ ] id )
{
try
{
foreach ( var key in id )
{
await this . companyService . DeleteAsync ( key ) ;
}
await this . unitOfWork . SaveChangesAsync ( ) ;
return Json ( new { success = true } ) ;
}
catch ( Exception e )
{
return Json ( new { success = false , err = e . GetBaseException ( ) . Message } ) ;
}
}
//保存datagrid编辑的数据
[ HttpPost ]
public async Task < JsonResult > AcceptChanges ( Company [ ] companies )
{
if ( ModelState . IsValid )
{
try
{
foreach ( var item in companies )
{
this . companyService . ApplyChanges ( item ) ;
}
var result = await this . unitOfWork . SaveChangesAsync ( ) ;
return Json ( new { success = true , result } ) ;
}
catch ( Exception e )
{
return Json ( new { success = false , err = e . GetBaseException ( ) . Message } ) ;
}
}
else
{
var modelStateErrors = string . Join ( " , ", ModelState.Keys.SelectMany(key => ModelState[key].Errors.Select(n => n.ErrorMessage)));
return Json ( new { success = false , err = modelStateErrors } ) ;
}
}
//导出Excel
[ HttpPost ]
public async Task < IActionResult > ExportExcel ( string filterRules = "" , string sort = "Id" , string order = "asc" )
{
var fileName = "compnay" + DateTime . Now . ToString ( "yyyyMMddHHmmss" ) + ".xlsx" ;
var stream = await this . companyService . ExportExcelAsync ( filterRules , sort , order ) ;
return File ( stream , "application/vnd.ms-excel" , fileName ) ;
}
//导入excel
[ HttpPost ]
public async Task < IActionResult > ImportExcel ( )
{
try
{
var watch = new Stopwatch ( ) ;
watch . Start ( ) ;
var total = 0 ;
if ( Request . Form . Files . Count > 0 )
{
for ( var i = 0 ; i < this . Request . Form . Files . Count ; i ++ )
{
var model = Request . Form [ "model" ] . FirstOrDefault ( ) ?? "company" ;
var folder = Request . Form [ "folder" ] . FirstOrDefault ( ) ?? "company" ;
var autosave = Convert . ToBoolean ( Request . Form [ "autosave" ] . FirstOrDefault ( ) ) ;
var properties = ( Request . Form [ "properties" ] . FirstOrDefault ( ) ?. Split ( ',' ) ) ;
var file = Request . Form . Files [ i ] ;
var filename = file . FileName ;
var contenttype = file . ContentType ;
var size = file . Length ;
var ext = Path . GetExtension ( filename ) ;
var path = Path . Combine ( this . _webHostEnvironment . ContentRootPath , "UploadFiles" , folder ) ;
if ( ! Directory . Exists ( path ) )
{
Directory . CreateDirectory ( path ) ;
}
var datatable = await NPOIHelper . GetDataTableFromExcelAsync ( file . OpenReadStream ( ) , ext ) ;
await this . companyService . ImportDataTableAsync ( datatable ) ;
await this . unitOfWork . SaveChangesAsync ( ) ;
total = datatable . Rows . Count ;
if ( autosave )
{
var filepath = Path . Combine ( path , filename ) ;
file . OpenReadStream ( ) . Position = 0 ;
using ( var stream = System . IO . File . Create ( filepath ) )
{
await file . CopyToAsync ( stream ) ;
}
}
}
}
watch . Stop ( ) ;
return Json ( new { success = true , total = total , elapsedTime = watch . ElapsedMilliseconds } ) ;
}
catch ( Exception e ) {
this . _logger . LogError ( e , " Excel导入失败" ) ;
return this . Json ( new { success = false , err = e . GetBaseException ( ) . Message } ) ;
}
}
//下载模板
public async Task < IActionResult > Download ( string file ) {
this . Response . Cookies . Append ( " fileDownload " , " true " ) ;
var path = Path . Combine ( this . _webHostEnvironment . ContentRootPath , file ) ;
var downloadFile = new FileInfo ( path ) ;
if ( downloadFile . Exists )
{
var fileName = downloadFile . Name ;
var mimeType = MimeTypeConvert . FromExtension ( downloadFile . Extension ) ;
var fileContent = new byte [ Convert . ToInt32 ( downloadFile . Length ) ] ;
using ( var fs = downloadFile . Open ( FileMode . Open , FileAccess . Read , FileShare . Read ) )
{
await fs . ReadAsync ( fileContent , 0 , Convert . ToInt32 ( downloadFile . Length ) ) ;
}
return this . File ( fileContent , mimeType , fileName ) ;
}
else
{
throw new FileNotFoundException ( $ "文件 {file} 不存在!" ) ;
}
}
}
}MVC Views Companies Index
@model SmartAdmin.Data.Models.Customer
@{
ViewData["Title"] = "客户信息";
ViewData["PageName"] = "customers_index";
ViewData["Heading"] = " < i class =' fal fa-window text-primary ' > </ i > 客户信息";
ViewData["Category1"] = "组织架构";
ViewData["PageDescription"] = "";
}
< div class =" row " >
< div class =" col-lg-12 col-xl-12 " >
< div id =" panel-1 " class =" panel " >
< div class =" panel-hdr " >
< h2 >
客户信息
</ h2 >
< div class =" panel-toolbar " >
< button class =" btn btn-panel bg-transparent fs-xl w-auto h-auto rounded-0 " data-action =" panel-collapse " data-toggle =" tooltip " data-offset =" 0,10 " data-original-title =" Collapse " > < i class =" fal fa-window-minimize " > </ i > </ button >
< button class =" btn btn-panel bg-transparent fs-xl w-auto h-auto rounded-0 " data-action =" panel-fullscreen " data-toggle =" tooltip " data-offset =" 0,10 " data-original-title =" Fullscreen " > < i class =" fal fa-expand " > </ i > </ button >
</ div >
</ div >
< div class =" panel-container show " >
< div class =" panel-content py-2 rounded-bottom border-faded border-left-0 border-right-0 text-muted bg-subtlelight-fade " >
< div class =" row no-gutters align-items-center " >
< div class =" col " >
<!-- 开启授权控制请参考 @@if (Html.IsAuthorize("Create") -->
< div class =" btn-group btn-group-sm " >
< button onclick =" appendItem() " class =" btn btn-default " >
< span class =" fal fa-plus mr-1 " > </ span > 新增
</ button >
</ div >
< div class =" btn-group btn-group-sm " >
< button name =" deletebutton " disabled onclick =" removeItem() " class =" btn btn-default " >
< span class =" fal fa-times mr-1 " > </ span > 删除
</ button >
</ div >
< div class =" btn-group btn-group-sm " >
< button name =" savebutton " disabled onclick =" acceptChanges() " class =" btn btn-default " >
< span class =" fal fa-save mr-1 " > </ span > 保存
</ button >
</ div >
< div class =" btn-group btn-group-sm " >
< button name =" cancelbutton " disabled onclick =" rejectChanges() " class =" btn btn-default " >
< span class =" fal fa-ban mr-1 " > </ span > 取消
</ button >
</ div >
< div class =" btn-group btn-group-sm " >
< button onclick =" reload() " class =" btn btn-default " > < span class =" fal fa-search mr-1 " > </ span > 查询 </ button >
< button type =" button " class =" btn btn-default dropdown-toggle dropdown-toggle-split " data-toggle =" dropdown " aria-haspopup =" true " aria-expanded =" false " >
< span class =" sr-only " > Toggle Dropdown </ span >
</ button >
< div class =" dropdown-menu dropdown-menu-animated " >
< a class =" dropdown-item js-waves-on " href =" javascript:void() " > 我的记录 </ a >
< div class =" dropdown-divider " > </ div >
< a class =" dropdown-item js-waves-on " href =" javascript:void() " > 自定义查询 </ a >
</ div >
</ div >
< div class =" btn-group btn-group-sm hidden-xs " >
< button type =" button " onclick =" importExcel.upload() " class =" btn btn-default " > < span class =" fal fa-cloud-upload mr-1 " > </ span > 导入 </ button >
< button type =" button " class =" btn btn-default dropdown-toggle dropdown-toggle-split waves-effect waves-themed " data-toggle =" dropdown " aria-haspopup =" true " aria-expanded =" false " >
< span class =" sr-only " > Toggle Dropdown </ span >
</ button >
< div class =" dropdown-menu dropdown-menu-animated " >
< a class =" dropdown-item js-waves-on " href =" javascript:importExcel.downloadtemplate() " >
< span class =" fal fa-download " > </ span > 下载模板
</ a >
</ div >
</ div >
< div class =" btn-group btn-group-sm hidden-xs " >
< button onclick =" exportExcel() " class =" btn btn-default " >
< span class =" fal fa-file-export mr-1 " > </ span > 导出
</ button >
</ div >
</ div >
</ div >
</ div >
< div class =" panel-content " >
< div class =" table-responsive " >
< table id =" companies_datagrid " >
</ table >
</ div >
</ div >
</ div >
</ div >
</ div >
</ div >
<!-- 弹出窗体form表单 -->
< div id =" customerdetailwindow " class =" easyui-window "
title ="明细数据"
data-options =" modal:true,
closed:true,
minimizable:false,
collapsible:false,
maximized:false,
iconCls:'fal fa-window',
onBeforeClose:function(){
var that = $(this);
if(customerhasmodified()){
$.messager.confirm('确认','你确定要放弃保存修改的记录?',function(r){
if (r){
var opts = that.panel('options');
var onBeforeClose = opts.onBeforeClose;
opts.onBeforeClose = function(){};
that.panel('close');
opts.onBeforeClose = onBeforeClose;
hook = false;
}
});
return false;
}
},
onOpen:function(){
$(this).window('vcenter');
$(this).window('hcenter');
},
onRestore:function(){
},
onMaximize:function(){
}
" style =" width:820px;height:420px;display:none " >
<!-- toolbar -->
< div class =" panel-content py-2 rounded-bottom border-faded border-left-0 border-right-0 text-muted bg-subtlelight-fade sticky-top " >
< div class =" d-flex flex-row-reverse pr-4 " >
< div class =" btn-group btn-group-sm mr-1 " >
< button name =" saveitembutton " onclick =" savecustomeritem() " class =" btn btn-default " >
< i class =" fal fa-save " > </ i > 保存
</ button >
</ div >
< div class =" btn-group btn-group-sm mr-1 " id =" deleteitem-btn-group " >
< button onclick =" deletecustomeritem() " class =" btn btn-danger " >
< i class =" fal fa-trash-alt " > </ i > 删除
</ button >
</ div >
</ div >
</ div >
< div class =" panel-container show " >
< div class =" container " >
< div class =" panel-content " >
< form id =" customer_form "
class =" easyui-form form-horizontal p-1 "
method =" post "
data-options =" novalidate:true,
onChange: function(target){
hook = true;
$('button[name*='saveitembutton']').prop('disabled', false);
},
onLoadSuccess:function(data){
hook = false;
$('button[name*='saveitembutton']').prop('disabled', true);
} " >
@Html.AntiForgeryToken()
<!--Primary Key-->
@Html.HiddenFor(model = > model.Id)
< fieldset class =" form-group " >
<!-- begin row -->
<!--名称-->
< div class =" row h-100 justify-content-center align-items-center " >
< label class =" col-md-2 pr-1 form-label text-right " > < span class =" text-danger " > * </ span > @Html.DisplayNameFor(model = > model.Name) </ label >
< div class =" col-md-4 mb-1 pl-1 " >
< input id =" @Html.IdFor(model => model.Name) "
name =" @Html.NameFor(model => model.Name) "
value =" @Html.ValueFor(model => model.Name) "
tabindex =" 0 " required
class =" easyui-textbox "
style =" width:100% "
type =" text "
data-options =" prompt:'@Html.DescriptionFor(model => model.Name)',
required:true,
validType: 'length[0,50]'
" />
</ div >
< label class =" col-md-2 pr-1 form-label text-right " > @Html.DisplayNameFor(model = > model.Contect) </ label >
< div class =" col-md-4 mb-1 pl-1 " >
< input id =" @Html.IdFor(model => model.Contect) "
name =" @Html.NameFor(model => model.Contect) "
value =" @Html.ValueFor(model => model.Contect) "
tabindex =" 1 " required
class =" easyui-textbox "
style =" width:100% "
type =" text "
data-options =" prompt:'@Html.DescriptionFor(model => model.Contect)',
required:true,validType: 'length[0,12]'
" />
</ div >
< label class =" col-md-2 pr-1 form-label text-right " > @Html.DisplayNameFor(model = > model.PhoneNumber) </ label >
< div class =" col-md-4 mb-1 pl-1 " >
< input id =" @Html.IdFor(model => model.PhoneNumber) "
name =" @Html.NameFor(model => model.PhoneNumber) "
value =" @Html.ValueFor(model => model.PhoneNumber) "
tabindex =" 2 "
class =" easyui-textbox "
style =" width:100% "
type =" text "
data-options =" prompt:'@Html.DescriptionFor(model => model.PhoneNumber)',
required:false,validType: 'length[0,20]'
" />
</ div >
< label class =" col-md-2 pr-1 form-label text-right " > @Html.DisplayNameFor(model = > model.Address) </ label >
< div class =" col-md-4 mb-1 pl-1 " >
< input id =" @Html.IdFor(model => model.Address) "
name =" @Html.NameFor(model => model.Address) "
value =" @Html.ValueFor(model => model.Address) "
tabindex =" 3 "
class =" easyui-textbox "
style =" width:100% "
type =" text "
data-options =" prompt:'@Html.DescriptionFor(model => model.Address)',
required:true, validType: 'length[0,50]'
" />
</ div >
</ div >
</ fieldset >
</ form >
</ div >
</ div >
</ div >
</ div >
@await Component.InvokeAsync("ImportExcel", new ImportExcelOptions
{
entity = "Customer",
folder = "Customers",
url = "/Customers/ImportExcel",
tpl = "/Customers/Download"
})
@section HeadBlock {
< link href =" ~/css/notifications/toastr/toastr.css " rel =" stylesheet " asp-append-version =" true " />
< link href =" ~/css/formplugins/bootstrap-daterangepicker/bootstrap-daterangepicker.css " rel =" stylesheet " asp-append-version =" true " />
< link href =" ~/js/easyui/themes/insdep/easyui.css " rel =" stylesheet " asp-append-version =" true " />
}
@section ScriptsBlock {
< script src =" ~/js/dependency/numeral/numeral.min.js " asp-append-version =" true " > </ script >
< script src =" ~/js/dependency/moment/moment.js " asp-append-version =" true " > </ script >
< script src =" ~/js/notifications/toastr/toastr.js " > </ script >
< script src =" ~/js/formplugins/bootstrap-daterangepicker/bootstrap-daterangepicker.js " asp-append-version =" true " > </ script >
< script src =" ~/js/easyui/jquery.easyui.min.js " asp-append-version =" true " > </ script >
< script src =" ~/js/easyui/plugins/datagrid-filter.js " asp-append-version =" true " > </ script >
< script src =" ~/js/easyui/plugins/columns-ext.js " asp-append-version =" true " > </ script >
< script src =" ~/js/easyui/plugins/columns-reset.js " asp-append-version =" true " > </ script >
< script src =" ~/js/easyui/locale/easyui-lang-zh_CN.js " asp-append-version =" true " > </ script >
< script src =" ~/js/easyui/jquery.easyui.component.js " asp-append-version =" true " > </ script >
< script src =" ~/js/plugin/filesaver/FileSaver.js " asp-append-version =" true " > </ script >
< script src =" ~/js/plugin/jquery.serializejson/jquery.serializejson.js " asp-append-version =" true " > </ script >
< script src =" ~/js/jquery.custom.extend.js " asp-append-version =" true " > </ script >
< script src =" ~/js/jquery.extend.formatter.js " asp-append-version =" true " > </ script >
< script >
var $dg = $ ( '#companies_datagrid' ) ;
var EDITINLINE = true ;
var customer = null ;
var editIndex = undefined ;
//下载Excel导入模板
//执行导出下载Excel
function exportExcel ( ) {
const filterRules = JSON . stringify ( $dg . datagrid ( 'options' ) . filterRules ) ;
console . log ( filterRules ) ;
$ . messager . progress ( { title : '请等待' , msg : '正在执行导出...' } ) ;
let formData = new FormData ( ) ;
formData . append ( 'filterRules' , filterRules ) ;
formData . append ( 'sort' , 'Id' ) ;
formData . append ( 'order' , 'asc' ) ;
$ . postDownload ( '/Customers/ExportExcel' , formData ) . then ( res => {
$ . messager . progress ( 'close' ) ;
toastr . success ( '导出成功!' ) ;
} ) . catch ( err => {
//console.log(err);
$ . messager . progress ( 'close' ) ;
$ . messager . alert ( '导出失败' , err . statusText , 'error' ) ;
} ) ;
}
//弹出明细信息
function showDetailsWindow ( id , index ) {
const customer = $dg . datagrid ( 'getRows' ) [ index ] ;
openCustomerDetailWindow ( customer , 'Modified' ) ;
}
function reload ( ) {
$dg . datagrid ( 'uncheckAll' ) ;
$dg . datagrid ( 'reload' ) ;
}
//新增记录
function appendItem ( ) {
customer = {
Address : '-' ,
RegisterDate : moment ( ) . format ( 'YYYY-MM-DD HH:mm:ss' ) ,
} ;
if ( ! EDITINLINE ) {
//弹出新增窗口
openCustomerDetailWindow ( customer , 'Added' ) ;
} else {
if ( endEditing ( ) ) {
//对必填字段进行默认值初始化
$dg . datagrid ( 'insertRow' ,
{
index : 0 ,
row : customer
} ) ;
editIndex = 0 ;
$dg . datagrid ( 'selectRow' , editIndex )
. datagrid ( 'beginEdit' , editIndex ) ;
hook = true ;
}
}
}
//删除编辑的行
function removeItem ( ) {
if ( this . $dg . datagrid ( 'getChecked' ) . length <= 0 && EDITINLINE ) {
if ( editIndex !== undefined ) {
const delindex = editIndex ;
$dg . datagrid ( 'cancelEdit' , delindex )
. datagrid ( 'deleteRow' , delindex ) ;
hook = true ;
$ ( "button[name*='savebutton']" ) . prop ( 'disabled' , false ) ;
$ ( "button[name*='cancelbutton']" ) . prop ( 'disabled' , false ) ;
} else {
const rows = $dg . datagrid ( 'getChecked' ) ;
rows . slice ( ) . reverse ( ) . forEach ( row => {
const rowindex = $dg . datagrid ( 'getRowIndex' , row ) ;
$dg . datagrid ( 'deleteRow' , rowindex ) ;
hook = true ;
} ) ;
}
} else {
deletechecked ( ) ;
}
}
//删除选中的行
function deleteChecked ( ) {
const checked = $dg . datagrid ( 'getChecked' ) . filter ( item => item . Id != null && item . Id > 0 ) . map ( item => {
return item . Id ;
} ) ; ;
if ( checked . length > 0 ) {
deleteRows ( checked ) ;
} else {
$ . messager . alert ( '提示' , '请先选择要删除的记录!' , 'question' ) ;
}
}
//执行删除
function deleteRows ( selected ) {
$ . messager . confirm ( '确认' , `你确定要删除这 <span class='badge badge-icon position-relative'> ${ selected . length } </span> 行记录?` , result => {
if ( result ) {
$ . post ( '/Customers/DeleteChecked' , { id : selected } )
. done ( response => {
if ( response . success ) {
toastr . error ( `成功删除 [ ${ selected . length } ] 行记录` ) ;
reload ( ) ;
} else {
$ . messager . alert ( '错误' , response . err , 'error' ) ;
}
} )
. fail ( ( jqXHR , textStatus , errorThrown ) => {
$ . messager . alert ( '异常' , ` ${ jqXHR . status } : ${ jqXHR . statusText } ` , 'error' ) ;
} ) ;
}
} ) ;
}
//开启编辑状态
function onClickCell ( index , field ) {
customer = $dg . datagrid ( 'getRows' ) [ index ] ;
const _actions = [ 'action' , 'ck' ] ;
if ( ! EDITINLINE || $ . inArray ( field , _actions ) >= 0 ) {
return ;
}
if ( editIndex !== index ) {
if ( endEditing ( ) ) {
$dg . datagrid ( 'selectRow' , index )
. datagrid ( 'beginEdit' , index ) ;
hook = true ;
editIndex = index ;
const ed = $dg . datagrid ( 'getEditor' , { index : index , field : field } ) ;
if ( ed ) {
( $ ( ed . target ) . data ( 'textbox' ) ? $ ( ed . target ) . textbox ( 'textbox' ) : $ ( ed . target ) ) . focus ( ) ;
}
} else {
$dg . datagrid ( 'selectRow' , editIndex ) ;
}
}
}
//关闭编辑状态
function endEditing ( ) {
if ( editIndex === undefined ) {
return true ;
}
if ( this . $dg . datagrid ( 'validateRow' , editIndex ) ) {
$dg . datagrid ( 'endEdit' , editIndex ) ;
return true ;
} else {
const invalidinput = $ ( 'input.validatebox-invalid' , $dg . datagrid ( 'getPanel' ) ) ;
const fieldnames = invalidinput . map ( ( index , item ) => {
return $ ( item ) . attr ( 'placeholder' ) || $ ( item ) . attr ( 'id' ) ;
} ) ;
$ . messager . alert ( '提示' , ` ${ Array . from ( fieldnames ) } 输入有误.` , 'error' ) ;
return false ;
}
}
//提交保存后台数据库
function acceptChanges ( ) {
if ( endEditing ( ) ) {
if ( $dg . datagrid ( 'getChanges' ) . length > 0 ) {
const inserted = $dg . datagrid ( 'getChanges' , 'inserted' ) . map ( item => {
item . TrackingState = 1 ;
return item ;
} ) ;
const updated = $dg . datagrid ( 'getChanges' , 'updated' ) . map ( item => {
item . TrackingState = 2
return item ;
} ) ;
const deleted = $dg . datagrid ( 'getChanges' , 'deleted' ) . map ( item => {
item . TrackingState = 3
return item ;
} ) ;
//过滤已删除的重复项
const changed = inserted . concat ( updated . filter ( item => {
return ! deleted . includes ( item ) ;
} ) ) . concat ( deleted ) ;
//$.messager.progress({ title: '请等待', msg: '正在保存数据...', interval: 200 });
$ . post ( '/Customers/AcceptChanges' , { customers : changed } )
. done ( response => {
//$.messager.progress('close');
//console.log(response);
if ( response . success ) {
toastr . success ( '保存成功' ) ;
$dg . datagrid ( 'acceptChanges' ) ;
reload ( ) ;
hook = false ;
} else {
$ . messager . alert ( '错误' , response . err , 'error' ) ;
}
} )
. fail ( ( jqXHR , textStatus , errorThrown ) => {
//$.messager.progress('close');
$ . messager . alert ( '异常' , ` ${ jqXHR . status } : ${ jqXHR . statusText } ` , 'error' ) ;
} ) ;
}
}
}
function rejectChanges ( ) {
$dg . datagrid ( 'rejectChanges' ) ;
editIndex = undefined ;
hook = false ;
}
$ ( document ) . ready ( function ( ) {
//定义datagrid结构
$dg . datagrid ( {
rownumbers : true ,
checkOnSelect : false ,
selectOnCheck : false ,
idField : 'Id' ,
sortName : 'Id' ,
sortOrder : 'desc' ,
remoteFilter : true ,
singleSelect : true ,
method : 'get' ,
onClickCell : onClickCell ,
clientPaging : false ,
pagination : true ,
striped : true ,
filterRules : [ ] ,
onHeaderContextMenu : function ( e , field ) {
e . preventDefault ( ) ;
$ ( this ) . datagrid ( 'columnMenu' ) . menu ( 'show' , {
left : e . pageX ,
top : e . pageY
} ) ;
} ,
onBeforeLoad : function ( ) {
const that = $ ( this ) ;
document . addEventListener ( 'panel.onfullscreen' , ( ) => {
setTimeout ( ( ) => {
that . datagrid ( 'resize' ) ;
} , 200 )
} )
} ,
onLoadSuccess : function ( data ) {
editIndex = undefined ;
$ ( "button[name*='deletebutton']" ) . prop ( 'disabled' , true ) ;
$ ( "button[name*='savebutton']" ) . prop ( 'disabled' , true ) ;
$ ( "button[name*='cancelbutton']" ) . prop ( 'disabled' , true ) ;
} ,
onCheck : function ( ) {
$ ( "button[name*='deletebutton']" ) . prop ( 'disabled' , false ) ;
} ,
onUncheck : function ( ) {
const checked = $ ( this ) . datagrid ( 'getChecked' ) . length > 0 ;
$ ( "button[name*='deletebutton']" ) . prop ( 'disabled' , ! checked ) ;
} ,
onSelect : function ( index , row ) {
customer = row ;
} ,
onBeginEdit : function ( index , row ) {
//const editors = $(this).datagrid('getEditors', index);
} ,
onEndEdit : function ( index , row ) {
editIndex = undefined ;
} ,
onBeforeEdit : function ( index , row ) {
editIndex = index ;
row . editing = true ;
$ ( "button[name*='deletebutton']" ) . prop ( 'disabled' , false ) ;
$ ( "button[name*='cancelbutton']" ) . prop ( 'disabled' , false ) ;
$ ( "button[name*='savebutton']" ) . prop ( 'disabled' , false ) ;
$ ( this ) . datagrid ( 'refreshRow' , index ) ;
} ,
onAfterEdit : function ( index , row ) {
row . editing = false ;
editIndex = undefined ;
$ ( this ) . datagrid ( 'refreshRow' , index ) ;
} ,
onCancelEdit : function ( index , row ) {
row . editing = false ;
editIndex = undefined ;
$ ( "button[name*='deletebutton']" ) . prop ( 'disabled' , true ) ;
$ ( "button[name*='savebutton']" ) . prop ( 'disabled' , true ) ;
$ ( "button[name*='cancelbutton']" ) . prop ( 'disabled' , true ) ;
$ ( this ) . datagrid ( 'refreshRow' , index ) ;
} ,
frozenColumns : [ [
/*开启CheckBox选择功能*/
{ field : 'ck' , checkbox : true } ,
{
field : 'action' ,
title : '操作' ,
width : 85 ,
sortable : false ,
resizable : true ,
formatter : function showdetailsformatter ( value , row , index ) {
if ( ! row . editing ) {
return `<div class="btn-group">
<button onclick="showDetailsWindow(' ${ row . Id } ', ${ index } )" class="btn btn-primary btn-sm btn-icon waves-effect waves-themed" title="查看明细" ><i class="fal fa-edit"></i> </button>
<button onclick="deleteRows([' ${ row . Id } '], ${ index } )" class="btn btn-primary btn-sm btn-icon waves-effect waves-themed" title="删除记录" ><i class="fal fa-times"></i> </button>
</div>` ;
} else {
return `<button class="btn btn-primary btn-sm btn-icon waves-effect waves-themed" disabled title="查看明细" ><i class="fal fa-edit"></i> </button>` ;
}
}
}
] ] ,
columns : [ [
{ /*名称*/
field : 'Name' ,
title : '@Html.DisplayNameFor(model => model.Name)' ,
width : 200 ,
hidden : false ,
editor : {
type : 'textbox' ,
options : { prompt : '@Html.DescriptionFor(model => model.Name)' , required : true , validType : 'length[0,128]' }
} ,
sortable : true ,
resizable : true
} ,
{ /*联系人*/
field : 'Contect' ,
title : '@Html.DisplayNameFor(model => model.Contect)' ,
width : 120 ,
hidden : false ,
editor : {
type : 'textbox' ,
options : { prompt : '@Html.DescriptionFor(model => model.Contect)' , required : true , validType : 'length[0,12]' }
} ,
sortable : true ,
resizable : true
} ,
{ /*电话*/
field : 'PhoneNumber' ,
title : '@Html.DisplayNameFor(model => model.PhoneNumber)' ,
width : 200 ,
hidden : false ,
editor : {
type : 'textbox' ,
options : { prompt : '@Html.DescriptionFor(model => model.PhoneNumber)' , required : false , validType : 'length[0,20]' }
} ,
sortable : true ,
resizable : true
} ,
{ /*地址*/
field : 'Address' ,
title : '@Html.DisplayNameFor(model => model.Address)' ,
width : 120 ,
hidden : false ,
editor : {
type : 'textbox' ,
options : { prompt : '@Html.DescriptionFor(model => model.Address)' , required : false , validType : 'length[0,50]' }
} ,
sortable : true ,
resizable : true ,
}
] ]
} ) . datagrid ( 'columnMoving' )
. datagrid ( 'resetColumns' )
. datagrid ( 'enableFilter' , [
] )
. datagrid ( 'load' , '/Customers/GetData' ) ;
}
) ;
</ script >
< script type =" text/javascript " >
//判断新增编辑状态
var MODELSTATE = 'Added' ;
var customerid = null ;
function openCustomerDetailWindow ( data , state ) {
MODELSTATE = state ;
initcustomerdetailview ( ) ;
customerid = ( data . Id || 0 ) ;
$ ( "#customerdetailwindow" ) . window ( "open" ) ;
$ ( '#customer_form' ) . form ( 'reset' ) ;
$ ( '#customer_form' ) . form ( 'load' , data ) ;
}
//删除当前记录
function deletecustomeritem ( ) {
$ . messager . confirm ( '确认' , '你确定要删除该记录?' , result => {
if ( result ) {
const url = `/Customers/Delete/ ${ customerid } ` ;
$ . get ( url ) . done ( res => {
if ( res . success ) {
toastr . success ( "删除成功" ) ;
$ ( "#customerdetailwindow" ) . window ( "close" ) ;
reload ( ) ;
} else {
$ . messager . alert ( "错误" , res . err , "error" ) ;
}
} ) ;
}
} ) ;
}
//async 保存数据
async function savecustomeritem ( ) {
const $customerform = $ ( '#customer_form' ) ;
if ( $customerform . form ( 'enableValidation' ) . form ( 'validate' ) ) {
let customer = $customerform . serializeJSON ( ) ;
let url = '/Customers/Edit' ;
//判断是新增或是修改方法
if ( MODELSTATE === 'Added' ) {
url = '/Customers/Create' ;
}
var token = $ ( 'input[name="__RequestVerificationToken"]' , $customerform ) . val ( ) ;
//$.messager.progress({ title: '请等待', msg: '正在保存数据...', interval: 200 });
$ . ajax ( {
type : "POST" ,
url : url ,
data : {
__RequestVerificationToken : token ,
customer : customer
} ,
dataType : 'json' ,
contentType : 'application/x-www-form-urlencoded; charset=utf-8'
} )
. done ( response => {
//$.messager.progress('close');
if ( response . success ) {
hook = false ;
$customerform . form ( 'disableValidation' ) ;
$dg . datagrid ( 'reload' ) ;
$ ( '#customerdetailwindow' ) . window ( "close" ) ;
toastr . success ( "保存成功" ) ;
} else {
$ . messager . alert ( "错误" , response . err , "error" ) ;
}
} )
. fail ( ( jqXHR , textStatus , errorThrown ) => {
//$.messager.progress('close');
$ . messager . alert ( '异常' , ` ${ jqXHR . status } : ${ jqXHR . statusText } ` , 'error' ) ;
} ) ;
}
}
//关闭窗口
function closecustomerdetailwindow ( ) {
$ ( '#customerdetailwindow' ) . window ( 'close' ) ;
}
//判断是否有没有保存的记录
function customerhasmodified ( ) {
return hook ;
}
function initcustomerdetailview ( ) {
//判断是否显示功能按钮
}
</ script >
}上記のビューレイヤーのコードは非常に複雑ですが、それらはすべて固定形式であり、足場ですばやく生成できます
Open startup.cs在public void configureServices(iservicecollection Services)レジスタサービスサービス。
services.addscoped <icustomerservice、customerservice>();
EFコアコードファースト移行
Visual Studio.netで
パッケージマネージャーコントララン
PM>:移行create_companyを追加します
PM>:databaseを更新します
PM>:移行
分散トランザクションのソリューションおよびアプリケーションシナリオをキャップします
nugetインストール
PM> install-package dotnetcore.cap
PM> install-package dotnetcore.cap.rabbitmq
PM> install-package dotnetcore.cap.sqlserver
public void ConfigureServices ( IServiceCollection services )
{
services . AddCap ( x =>
{
x . UseEntityFramework < SmartDbContext > ( ) ;
x . UseRabbitMQ ( "127.0.0.1" ) ;
x . UseDashboard ( ) ;
x . FailedRetryCount = 5 ;
x . FailedThresholdCallback = failed =>
{
var logger = failed . ServiceProvider . GetService < ILogger < Startup >> ( ) ;
logger . LogError ( $ @"A message of type {failed.MessageType} failed after executing {x.FailedRetryCount} several times,
requiring manual troubleshooting . Message name : { failed . Message . GetName ( ) } ");
} ;
} ) ;
}QQ群!